64ed6c3

Caliptra: A Datacenter System on a Chip (SoC) Root of Trust (RoT)
Revision 2.1
Version 0.1
Introduction
Caliptra1 was originally created as part of the Open Compute Project (OCP). The major revisions of the Caliptra specifications are published at OCP. The evolving source code and documentation for Caliptra are in this repository within theΒ CHIPS Alliance Project, a Series of LF Projects, LLC.
The objective of Caliptra is to define core RoT capabilities that must be implemented in the System on Chip (SoC) or ASIC of any device in a cloud platform. The collection of these RoT capabilities is referred to as the Silicon RoT Services (Silicon RoT).
Background
The overall security posture of silicon devices depends on establishing a core root of trust (RoT) and chain of trust. The core root of trust and chain of trust must attest to the integrity of configuration and mutable code.
Traditional RoT architectures offer many intrinsic security services and hosted security applications on a trusted execution environment (TEE). These architectures include (but are not limited to) hardware capabilities (cryptographic and microprocessor), ROM, firmware, and API infrastructure. These solutions are instantiated in discrete or integrated forms in various platform and component architectures.
Some of these solutions are either proprietary or aligned to specific parts of industry standards, consortium, or association specifications; for example, National Institutes of Standards and Technology (NIST), Open Compute Project (OCP), Trusted Computing Group (TCG), Distributed Management Task Force (DMTF), Institute of Electrical and Electronics Engineers (IEEE), etc. These solutions may be certified to conform to various standards; for example, the NIST cryptographic algorithm Validation program (CAVP).
Establishing a consistent RoT on very different hardware configurations while maintaining configuration and deployment flexibility is challenging. There is no uniform configuration across Cloud Service Providers (CSPs). For example, a system with host processors has very different firmware security measures when compared to a system without head-nodes or host processors.
The OCP Security WG specifications are making progress toward establishing the platform and peripheral security architecture recommendations that are necessary to attain the desired consistency in platform security orchestration.
Silicon RoT goals
To drive agility of specification definition and to maximize applicability, the scope of Caliptra is deliberately minimalistic. This minimalist approach drives industry alignment, consistency, and faster adoption of foundational device security primitives. A well and narrowly defined specification maximizes architectural composability; reusability across CSPs, products, and vendors; and feasibility of open sourcing.
Enhancements and advanced use cases and applications are outside the scope of this specification and may be developed in the form of a roadmap for the Silicon RoT and community engagement.
Caliptra 2.0 defines a design standard for a Silicon internal RoT baseline. This standard satisfies a Root of Trust for Measurement (RTM) and cryptographic services for the SoC. The SoC must measure the code and configuration it boots into Caliptra in this configuration. Caliptra must store these measurements and report them with signed attestations rooted in unique per-asset cryptographic entropy. As such, Caliptra serves as a Root of Trust for Identity (RTI) for the SoC.
The Caliptra Subsystem further standardizes SoC protection mechanisms with Root of Trust for Update (RTU) and Root of Trust for Recovery (RTRec). The open-source implementation of Caliptra drives transparency and consistency into the root of trust mechanisms that anchor foundational security services for the SoC.
Within this scope, the goals for a Caliptra 2.0 specification with subsystem include:
- Definition and design of the standard silicon internal RoT baseline:
- Reference functional specification:
- Scope including RTM, RTU and RTRec capabilities
- Control over SoC non-volatile state, including per asset entropy
- Reference APIs:
- Attestation APIs
- Authentication APIs
- Recovery APIs
- Internal SoC Cryptographic services
- Reference implementation
- Open source reference (including RTL and firmware reference code):
- For implementation consistency, using open source dynamics to avoid pitfalls and common mistakes
- For accelerated adoption, so that future products can leverage existing designs and avoid having to start the design process from scratch
- For greater transparency, to avoid fragmentation in the implementation space
- Firmware and RTL logical design are open, managed by consortium
- Reference functional specification:
- Consistency - across the industry in the internal RoT (iRoT) architecture and implementation:
- DICE identity, measurement, and recovery
- The silicon iRoT scope includes all datacenter-focused server class SoC / ASIC (datacenter focused) devices (SSD - DC, NIC, CPU, GPU - DC):
- Critical priority are devices with the ability to handle user plain text data
- Top priority are CPU SoCs
- Other examples include SmartNIC and accelerators
- 2.0 scope includes further datacenter devices such as
- SSD, HDD, BMC, DIMM, PSU, CPLD etc.
- Critical priority are devices with the ability to handle user plain text data
Note that Caliptra reference code (including RTL and firmware) is intended to be adopted as-is, without modification.
Explicitly out of scope is how silicon integration into backend work is performed such as:
- Foundry IP integration
- Physical design countermeasures
- Analog IPs
- Post manufacture test and initialization (OSAT)
- Certification
Use cases
The Silicon RoT use cases can be supported through the adoption of specific industry standards, and association and consortium specifications. For more information, see specific documents in References.
In this version of the specification, the desired capabilities address the basics of supply chain security use cases.
Supply chain security
- Mutable code integrity: The objective is to prove the device is running genuine firmware such that the device manufacturer can vouch for its authenticity and integrity, and the device owner can ensure only authorized updates are applied to the device. This flow is aligned with Reference 9 and can be achieved with dual signature verification of equal imposition.
- Configuration and lifecycle management: The objective is to allow the platform owner to securely configure the RoT capabilities, and to enable and authorize lifecycle state transitions of the SoC.
DICE Protection Environment
Caliptra implements the DICE Protection Environment (DPE) API, allowing it to derive and wield a DICE identity on behalf of other elements within the SoC. Use cases for this API include serving as a signing oracle for a Security Protocol and Data Model (SPDM) responder that is executing in a SoC application processor (in passive mode) or in the Manufacturer Control Unit (MCU in subsystem mode), as well as authentication to a discrete TPM device.
Industry standards and specifications
This specification follows the industry standards and specifications listed in References.
NIST SP 800-193 Platform Firmware Resiliency
Per Reference 1, RoT subsystems are required to fulfill three principles: protection, detection and recovery. The associated RoT services are referred to as:
- The Root of Trust for Update (RTU) is responsible for authenticating firmware updates and critical data changes to support platform protection capabilities.
- The Root of Trust for Detection (RTD) is responsible for firmware and critical data corruption detection capabilities.
- The Root of Trust for Recovery (RTRec) is responsible for recovery of firmware and critical data when corruption is detected, or when instructed by an administrator.
These RoT services can be hosted by a complex RoT as a whole or these services can be spread across one or more components within a platform. This determination has a basis in physical risk. Physical adversaries with reasonable skill can bypass a discrete RoTβs detection capabilities, for example, with SPI interposers.
However, a RoT embedded within a SoC or ASIC represents a much higher detection bar for a physical adversary to defeat. For this reason in Caliptra 2.0 Core, the cryptographic module shall deliver the Detection capability for itself while providing Measurement and Identity services for the rest of the SoC. The Measurement and Identity services that Caliptra provides can be used by the SoC to create Detection capability for the measured firmware and configuration data.
The objectives of Caliptra Core are minimalistic scope and maximum applicability. To that end, Update and Recovery are decoupled from Caliptra Core and are expected to be provided either by Caliptra 2.0 Subsystem or are expected to be provided by an external RoT subsystem, such as a discrete RoT board element on a datacenter platform (passive mode). Because a physical adversary can trivially nullify any recovery or update capabilities, no matter where implemented, decoupling represents no regression in a security posture, while enabling simplicity and applicability for the internal SoC Silicon RoT.
Detection of corrupted critical code and data (configuration) requires strong end to end cryptographic integrity verification. To meet the RTD requirements, Silicon RoT shall:
- Cryptographically verify & measure its code and configuration
- Sign these measurements with a unique attestation key
- Report measurements to a host or external entity, which can further verify the authenticity and integrity of the device (also known as attestation)
- Recovery follows Open Compute Project Secure Recovery flows and Streaming Boot. (FIXME: Add links to released specs; they are in draft mode now)
Measurements and Verification include Code and Configuration. Configuration includes invasive capabilities that impact the user service level agreement (SLA) on confidentiality; for example, the enablement of debug capabilities that grant an operator access to raw, unencrypted registers for any tenant context. In order to measure and attest configuration, the Silicon RoT must be in control of the configuration.
As an extension to controlling configuration, the Silicon RoT must control the security states (for more information, see Caliptra Security States). Certain security states by design grant full invasive capabilities to an external operator, for debug or field analysis.
Measurements must be uniquely bound to the device and its manufacturer at a minimum. This establishes the need for Identity services in the Silicon RoT, which serve as the basis for key derivation and attestation authenticity.
For further details about how Caliptra addresses NIST SP 800-193, see Device Resilience.
Trusted Computing Group (TCG) DICE Attestation
In accordance with OCP Attestation specification Reference 8, devices must have a cryptographic identity for the endorsement of attestation quotes. The RTM implementation follows TCG DICE (for information, see Reference 4, Reference 5, and Reference 6). One of the benefits of TCG DICE device identities is having renewable security. This renewability complements ownership transfer and circular economy. The new owner is not burdened with the identity of the previous owner, nor is the new owner burdened with trusting an irrevocable hardware identity certificate. This benefits the transferee, as their identities can be revoked through standard PKI mechanisms. DICE based certificates are fully compatible with Public Key Infrastructure (PKI), including full lifecycle management and PKI Certificate Revocation List (CRL).
Operational security during the manufacturing process is critical, to ensure the DICE entropy is securely initialized, certified, and registered. Operational security avoids any pilfering of this asset by eavesdroppers. Operational security is outside the scope of this specification.
Threat model
The Caliptra threat model describes attacker profiles, assets and attack surfaces, and paths to these assets based on attacker profiles. Subsections provide further details.
Threat scenarios as comprehended by assets and possible attack paths are as complete as possible but focus on the worst case scenarios. Thus not every attack path to asset is captured in this threat model.
Attacker profiles
An attacker profile is based on factors like the tools that are accessible to the attacker, the level of access to the target of evaluation, and expertise of the attacker to use these methods. These factors are described in the following tables.
Table 1: Tools accessible to attacker
| Attack tools | Type of attack | Purpose and usage |
|---|---|---|
| Debuggers, fuzzing devices, image reverse engineering tools, and software payloads | Logical fault injection | Find logical and functional vulnerabilities and exploit those to achieve unauthorized operations. |
| Clock fault injectors, voltage fault injectors, electromagnetic fault injectors, optical fault injectors, and micro probing | Environmental fault injection | Alter the execution flow of the critical decision points, especially in the early execution. |
| Power analyzers, timing analyzers (scopes, etc.), low speed bus analyzers, and optical emission analyzers | Side channel analysis | Infer security sensitive information by analyzing various operational conditions. |
| Microscopic imaging, reverse engineering, scanning electron microscope imaging, and focused ion beam (FIB) | Chip invasive attacks | Decapsulation, depackaging, and rebonding to probe the internals of the chip. |
Table 2: Type of access to level of access
| Type of access | Levels of access | Attack paths available |
|---|---|---|
| Physical access | Unrestricted access for physical and logical attacks. | Chip invasive attacks and chip non-invasive attacks |
| Remote access | Limited access for attacks with both privileged and unprivileged access rights. | Chip non-invasive attacks and network attacks |
Table 3: Definition of expertise (JIL)
| Proficiency level | Definition | Detailed definition |
|---|---|---|
| Expert | Can use chip invasive, fault injections, side channels, and logical tools. Understands hardware and software in depth. Familiar with implementation: - Algorithms - Protocols - Hardware structures - Principle and security concepts | Developer-level knowledge of algorithms, protocols, hardware structure, and principles. Understands techniques and tools for attacks. |
| Proficient | Can use fault injections, side channels, and logical tools. Has reasonable understanding of hardware and software. Familiar with security behavior. | Familiar with security behavior and classical attacks. |
| Layperson | No particular expertise. | No particular expertise. |
Types of attacks
Physical attacks
A physical attacker has full access to the electrical and physical components. This includes access to interfaces, connectors, and ports of the SoC/ASIC in which Caliptra is integrated without restriction.
Invasive attacks that involve depackaging or delayering of the SoC/ASIC are out-of-scope. Non-invasive attacks are in scope.
- Fault injection attacks
- Counter measurements - as strong recommendation
- Power and electromagnetic analysis attacks
- Counter measurements - as strong recommendation
Table 4: Attack types
| Attack | Description | Threat model scope |
|---|---|---|
| Electromagnetic β passive | Attacker observes the electromagnetic power spectrum and signals radiated from the product. | - Includes all attacks at all frequency ranges, including radio frequencies, infrared, optical, and ultraviolet. - Excludes attacks that require removing the package lid. |
| Electromagnetic β active | Attacker directs electromagnetic radiation at the product or portions of the product. | - Includes all attacks at all frequency ranges, including radio frequencies, infrared, optical, and ultraviolet. - Excludes attacks that require removing the package lid. |
| Electric β passive | Attacker probes the external pins of the package and observes electrical signals and characteristics including capacitance, current, and voltage signal. | - Includes both analog attacks and digital signal attacks. - Excludes attacks that require removing the package lid. |
| Electric β active | Attacker alters the electrical signal or characteristics of external pins. | - Includes both analog attacks and digital signal attacks. - Excludes attacks that require removing the package lid. |
| Temperature β passive | Attacker observes the temperature of the product or portions of the product. | Excludes attacks that require removing the package lid. |
| Temperature β active | Attacker applies external heat sources or sinks to alter the temperature of the product, possibly in a rapid fashion. | - Includes all temperature ranges (for example, pouring liquid nitrogen over the package or heating the package to above 100 C). - Excludes attacks that require removing the package lid. |
| Sound - passive | Attacker observes the sounds emitted by the product. | - Includes all frequencies. - Excludes attacks that require removing the package lid. |
Table 5: Logical attacks
| Attack | Description | Threat model scope |
|---|---|---|
| Debug and register interfaces | Manipulation of externally accessible registers of Caliptra. | Includes all buses that are accessible to components external to Caliptra, including JTAG. |
| Software interfaces | Attacker invokes software interfaces that are exposed by Caliptra to external components. | Includes all externally exposed software interfaces from both non-RoT firmware as well as interfaces accessed by external IP blocks. Includes exploiting both design and implementation flaws. For high value assets only (see next subsection), the attacker is assumed to fully control all mutable code of the SoC, including privileged Caliptra mutable code. |
| Side channel - timing | Attacker observes the elapsed time of different sensitive operations. | Includes attacks where the attacker actively stimulates the sensitive operations while timing. |
| Cryptographic analysis | Attacker observes plaintext, ciphertext, related data, or immediate values in cryptography to overcome cryptographic controls. | Includes all practical cryptanalysis attacks. Assumes NIST-unapproved algorithms provide no security (for example, SHA-1, Single DES, ChaCha20). Assumes any cryptographic algorithm that provides less than 128 bits of security (as determined by NIST SP 800-57) provides no security. |
Trust boundaries
The following figure shows trust boundaries for the discussion of threat modeling. SoCs based on Caliptra are expected to have Caliptra as silicon RoT, and are expected to have a platform or SoC security engine to orchestrate SoC security needs for the rest of the SoC.
Trust levels of Caliptra and the SoC security engine are not hierarchical. These two entities are responsible for different security aspects of the chip.
Figure 1: Caliptra trust boundaries

Caliptra interactions
The Caliptra Core blocks consume the Tc and Tcw trust level components. This boundary includes crypto accelerators, hardware key sequencer, key vault, Caliptra microcontroller, ROM, and subsystem interconnects. The Caliptra Core provides deterministic Caliptra behavior. Caliptra Core interacts with components in the Tse and Trs trust levels; while Caliptra Subsystem abosrbs the Tse functions.
Caliptra assets and threats
Assets are defined to be secrets or abilities that must be protected by an owner or user of the asset. Ownership means that the owner has the responsibility to protect these assets and must only make them available based on a defined mechanism while protecting all other assets.
An example of when an owner must protect assets is moving from secure mode to insecure mode. Another example is moving from one owner to another. Before moving through these transitions, the owner must ensure all assets are removed, disabled, or protected based on how the use case is defined.
Table 6: Assets
| Asset category | Asset | Security property | Attack profile | Attack path | Mitigation |
|---|---|---|---|---|---|
| Fuse/OTP high value secrets | UDS Seed | Confidentiality and integrity | Expert | Malicious manufacturing spoofing of UDS Seeds | UDS obfuscation with class RTL key |
| Fuse/OTP high value secrets | UDS Seed | Confidentiality and integrity | Expert | Invasive attack (fuse analysis) | Shield fuse IP |
| Fuse/OTP high value secrets | UDS Seed | Confidentiality and integrity | Expert | Boot path tampering while retrieving UDS values | UDS obfuscation with class RTL key |
| Fuse/OTP high value secrets | UDS Seed | Confidentiality and integrity | Expert | Attempting to derive die specific keys by knowing UDS | Confine unobfuscated UDS and subsequent derivations to key vault |
| Fuse/OTP high value secrets | Field entropy | Confidentiality and integrity | Expert | Malicious manufacturing spoofing on field entropy | Field entropy obfuscation with class RTL key |
| Fuse/OTP high value secrets | Field entropy | Confidentiality and integrity | Expert | Invasive attack (fuse analysis) | Shield fuse IP |
| Fuse/OTP high value secrets | Field entropy | Confidentiality and integrity | Expert | Boot path tampering while retrieving field entropy values | Field entropy obfuscation with class RTL key |
| Fuse/OTP high value secrets | Field entropy | Confidentiality and integrity | Expert | Attempting to derive die specific keys by knowing field entropy | Confine unobfuscated field entropy and subsequent derivations to key vault |
| Fuse/OTP high value secrets | FW authentication keys | Integrity | Proficient | Glitching | 1. Redundant decision making on critical code execution 2. Error check before consuming values from fuses 3. Environmental monitoring and protection |
| Fuse/OTP high value secrets | Versioning information from fuses | Integrity | Proficient | Glitching | Environmental monitoring and protection |
| Fuse/OTP high value secrets | IDEVID CERT chain | Integrity | Proficient | Glitching | 1. Environmental monitoring and protection 2. Error check before consuming values from fuse in various ways |
| Fuse/OTP high value secrets | OCP L.O.C.K. HEK RATCHET SEED | Confidentiality | Proficient | 1. Glitching 2. Software writing to modify the valid seed | 1. Consuming it after obfuscating with class RTL key 2. Environmental monitoring and protection 3. Error check before consuming values from fuse in various ways |
| Die unique assets | UDS (802.1AR Unique Device Secret) | Confidentiality and integrity | Proficient | 1. Software reading actual secrets 2. Side channel attack to infer secret | 1. Secrets locked in key vault, not readable by software 2. SCA protections |
| Die unique assets | CDIn (DICE compound device identifier for Layer n) | Confidentiality and integrity | Proficient | 1. Software reading actual secrets 2. Side channel attack to infer secret | 1. Secrets locked in key vault, not readable by software 2. SCA protections |
| Die unique assets | IDevIDPriv | Confidentiality and integrity | Proficient | 1. Software reading actual secrets 2. Side channel attack to infer secret | 1. Secrets locked in key vault, not readable by software 2. SCA protections |
| Die unique assets | LDevIDPriv | Confidentiality and integrity | Proficient | 1. Software reading actual secrets 2. Side channel attack to infer secret | 1. Secrets locked in key vault, not readable by software 2. SCA protections |
| Die unique assets | Obfuscation key | Confidentiality and integrity | Proficient | 1. Software reading actual secrets 2. Side channel attack to infer secret | 1. Secrets locked in key vault, not readable by software 2. SCA protections |
| Die unique assets | AliasFMC_KeyPriv | Confidentiality and integrity | Proficient | 1. Software reading actual secrets 2. Side channel attack to infer secret | 1. Secrets locked in key vault, not readable by software 2. SCA protections |
| Die unique assets | AliasRT_KeyPriv | Confidentiality and integrity | Proficient | 1. Software reading actual secrets 2. Side channel attack to infer secret | 1. Secrets locked in key vault, not readable by software 2. SCA protections |
| Root of trust execution | ROM FW | Integrity | Proficient | Glitching | 1. Redundant decision making on critical code execution 2. Environmental monitoring and protection |
| Root of trust execution | Execution unauthorized runtime FW | Authenticity and integrity | Proficient | Modify boot media | Authenticity and integrity check using PKI DSA upon power on |
| Root of trust execution | Execution unauthorized runtime FW | Authenticity and integrity | Proficient | Arbitrary payload pushed into execution | Authenticity and integrity check using PKI DSA during software updates and power on |
| Root of trust execution | Rollback Attack | Versioning | Proficient | 1. Modify boot media to host older versions 2. Bypass version check during boot | 1. Authenticity and integrity check using PKI DSA upon power on 2. Failproof, audited boot code implementation responsible for loading images |
| Root of trust execution | Control flow | Integrity and confidentiality if applicable | Proficient | 1. Return and jump addresses manipulation 2. Return values and errors tampering 3. Stack overflow 4. Buffer overflows 5. Privilege escalations and hijacking | 1. Various control flow integrity measures 2. Secure coding practices and auditing implementation |
| Boot measurements protected by Caliptra | Boot measurements that Caliptra gathers, stores, and reports | Integrity | Expert | 1. Manipulate measurements AiTM while in transit to Caliptra 2. SoC sends manipulated measurements to Caliptra | |
| Caliptra inputs | Security state | Integrity | Proficient | Glitching | Environmental monitoring and protection |
| Caliptra inputs | Mode selection (Boot Media Integrated and dependent selections) | Integrity | Proficient | Glitching | Environmental monitoring and protection |
| Caliptra inputs | AXI_USER attribute | Integrity | Proficient | Glitching | Environmental monitoring and protection |
| Caliptra inputs | Design-for-Test (DFT) and Design-for-Debug (DFD) | Integrity | Proficient | 1. Attempt to manipulate RoT execution via DFT or DFD flows to flows that are not plan-of-record 2. Attempt to retrieve device secrets via DFT or DFD flows when product is field-deployed 3. Attempt to retrieve device secrets via DFT or DFD flows while the product is being developed and debugged | Implement scan mode and debug unlock management within Caliptra with the required SoC support |
High level architecture
The following figure shows the basic high-level blocks of Caliptra.
Figure 2: Caliptra high level blocks

See the hardware section for a detailed discussion.
From Caliptra 2.x onwards, Caliptra introduces two modes of operation. Passive mode which was supported in 1.x architecture and Subsystem mode. Fundamental difference between passive mode and subsystem mode is that in the subsystem mode Caliptra is the RoT for the SoC and provides streaming boot, secure boot and attestation. In Subsystem mode, Caliptra also provides various crypto API services such as encryption/decryption of SoC FWs, Key releases, Key wraps, hashing etc. to name a few. Please see Caliptra subsystem mode Crypto API section for more details (FIXME: section name & details).
Passive Mode High Level Flow
Caliptra is among the first microcontrollers taken out of reset by the power-on reset logic. Caliptra coordinates the start of the firmware chain-of-trust with the immutable component of the SoC ROM. After the Caliptra ROM completes initialization, it provides a "stash measurement" API and callback signals for the SoC ROM (passive mode) to proceed with the boot process. Caliptra ROM supports stashing of at most eight measurements prior to the boot of Caliptra RT firmware. The SoC then may choose to boot Caliptra firmware. Any security-sensitive code or configuration loaded by the SoC prior to Caliptra firmware boot must be stashed within Caliptra. If the SoC exceeds Caliptra ROM's measurement stash capacity, attestation must be disabled until the next cold reset. The boot process is as follows:
- Hardware executes SoC power-on reset logic. This logic starts the execution of SoC ROM and Caliptra ROM.
- SoC ROM waits for the ready_for_fw signal from Caliptra ROM.
- SoC ROM fetches the FMC.
- If the FMC is Caliptra firmware:
- SoC ROM loads the Caliptra firmware into the Caliptra mailbox and issues the Caliptra "firmware load" command.
- Caliptra ROM authenticates, measures, and starts the Caliptra firmware.
- If the FMC is not Caliptra firmware:
- SoC ROM measures that firmware as the SoC FMC, and issues the Caliptra "stash measurement" command prior to executing SoC FMC.
- If the FMC is Caliptra firmware:
- SoC ROM executes SoC FMC.
- SoC FMC continues the boot process, forming a boot firmware chain-of-trust: each firmware fetches, authenticates, measures, and executes the next firmware needed to establish the device operating environment. Each firmware deposits the next firmware's measurements into Caliptra prior to execution. The exception is Caliptra's firmware: SoC firmware delegates the measurement and execution of Caliptra's firmware to Caliptra ROM.
- Upon eventual initialization, Caliptra firmware presents attestation APIs using the deposited measurements.
See Error Reporting and Handling for details about Caliptra and SoC firmware load and verification error handling.
Figure 3: Passive Caliptra boot flow

Subsystem Mode Boot Flow
MCU (Manufacturer Control Unit), that is holds platform & SoC specific FW and Caliptra are among the first microcontrollers taken out of reset by the power-on reset logic. Caliptra is responsible for the start of the firmware chain-of-trust with the immutable component of the MCU ROM. After the Caliptra ROM completes initialization, it provides a "stash measurement" API and callback signals for MCU ROM (subsystem mode) to proceed with the boot process. Caliptra ROM supports stashing of at most eight measurements prior to the boot of Caliptra RT firmware. Then Caliptra FW is loaded through OCP streaming boot flow. The OCP streaming boot flow uses an I3C controller with commands defined by the OCP Recovery specification. This controller constitutes the streaming boot interface in the boot flow below. Any security-sensitive code (eg. PLL programming) or configuration (eg. Fuse based Patching) loaded by the MCU prior to Caliptra firmware boot must be stashed within Caliptra. If the MCU exceeds Caliptra ROM's measurement stash capacity, attestation must be disabled until the next cold reset.
Note: This is extremely high level flow, please see the Subsystem Mode Section below for next level specifics.
The high level boot process is as follows:
- Hardware executes SoC power-on reset logic. This logic starts the execution of MCU ROM and Caliptra ROM.
- Streaming boot interface is gated until ready for recovery is written into streaming boot interface registers from Caliptra ROM. This happens at the same time as passive mode's ready_for_fw signal.
- Caliptra firmware is streamed & then pulled into Caliptra MB SRAM through the OCP streaming boot interface by a platform component (typically a BMC-like component). 1. Caliptra ROM authenticates, measures, and activates the Caliptra firmware.
- SoC manifest is streamed next via the streaming boot interface, which Caliptra authenticates & measures
- This is followed by MCU RT FW through the streaming boot protocol which Caliptra routes to MCU SRAM, authorizes and activates MCU to execute it.
- MCU RT FW will go through MCTP enumeration and fetch the remaining SoC blobs (FW, data etc.) using DSP0267 PLDM for Firmware Update over MCTP and uses Caliptra to authorize each of them. Note that MCU may also retrieve some non-FW blobs from a NVM while using Caliptra to perform security operations like integrity verification, decryption etc.
FIXME: ADD a pic
Identity
Caliptra must provide its runtime (RT) code with a cryptographic identity in accordance with the TCG DICE specification. This identity must be rooted in ROM, and provides an attestation over the security state of the RTM as well as the code that the RTM booted.
To ensure quantum-resistant RTM, each certificate includes a dual signatures based on ECC Secp384r1 and PQC MLDSA-87.
Figure 4: DICE Cert/Key generation

UDS
A combination of mask ROM and HW macros must implement the DICE key derivation and power-on latch, hiding the UDS seed and only making the CDI-derived signing key 'handle' visible to ROM. Real UDS will only be calculated during the cold boot in hardware, used for CDI derivation and immediately gets cleared.
The Caliptra UDS seed is stored as ciphertext in fuses, deobfuscated only on cold boot using a obfuscation key2 known only to the Caliptra Hardware. Once read by Caliptra HW at boot, the unobfuscated UDS is then used to derive the IDevID identity and immediately cleared by hardware.
IDevID key
Caliptra's IDevID key is a hardware identity generated by Caliptra ROM during manufacturing. This key "handle" must be solely wielded by Caliptra ROM, and shall never be exposed externally at any phase of the Caliptra lifecycle. IDevID is used to endorse LDevID. Caliptra supports both classic and post-quantum algorithms for endorsement based on ECDSA Secp384r1 and PQC MLDSA-87, respectively. The IDevID certificate is endorsed by the vendorβs provisioning CA (pCA) that is implemented via a HSM appliance connected to High Volume Manufacturing (HVM) flows (see provisioning CA in Reference 8).
See Provisioning IDevID During Manufacturing for further details on IDevID provisioning.
LDevID key
Caliptra shall support field-programmable entropy, which factors into the device's LDevID identity. The LDevID certificate is endorsed by IDevID and in turn endorses the FMC alias key. Caliptra supports both classic and post-quantum algorithms for endorsement based on ECDSA Secp384r1 and PQC MLDSA-87, respectively.
Caliptra's field-programmable entropy shall consist of two 16-byte slots. All slots are used to derive LDevID. An owner may decide to program as few or as many slots as they wish. Upon programming new entropy, on the next reset the device begins wielding its fresh LDevID. Owners need to validate the new LDevID by using IDevID.
Commentary: threat analysis
An ideal IDevID has the following properties:
- Cannot be altered by entities besides Caliptra in manufacturing security state.
- Cannot be impersonated by entities that are not a Caliptra instatiation for that device class.
- Cannot be cloned to additional devices of the same class.
- Private component cannot be extracted from Caliptra.
Caliptra 2.0 provides integrity over IDevID Certificate Signing Requests (CSRs).
Caliptra 1.0 alone does not fully address these properties. For example, a person-in-the-middle supply chain adversary could impersonate Caliptra by submitting its own IDevID CSR to the pCA. Vendors should threat model the IDevID generation and endorsement flows for their SoC. Threat actors to consider are the following:
- Components involved in UDS injection flows: can they inject the same obfuscated UDS to multiple devices, or to devices of different classes? Can they wield the obfuscation key to leak the UDS?
- Components servicing the connectivity between the Caliptra instantiation and the HSM applicance performing IDevID endorsement: can they alter or impersonate Caliptra's IDevID CSR?
- Physical attackers: see Physical Attack Countermeasures.
Vendors have incentives to mitigate these threats. The vendor identity chain secures RMA and confidential computing workflows.
Ultimately though, IDevID is not renewable. Renewable security, often referred to as trusted computing base recovery, is a base design principle in Caliptra. Therefore, it is a design goal to reduce the operational dependency on IDevID. Field entropy and LDevID satisfy this need.
Field entropy is a limited resource, consisting of only two 16-byte slots of one-time programmable fuses. It is not generally expected that a second-hand purchaser can program all or even any of these slots. Caliptra's DICE identity remains usable even after all field entropy slots are programmed, so this feature does not preclude a circular economy. Field entropy is a feature primarily designed for users who purchase new parts.
Field entropy and LDevID are intended to hedge against attackers with the following abilities:
- Can obtain UDS before the part first arrives at the owner's facility.
- Can wield that stolen UDS to impersonate the part after it is deployed within the owner's facility. The attacker can derive and wield IDevID to mint an attacker-controlled DICE hierarchy; for example, LDevID, FMC alias key, or Runtime firmware alias key.
- Cannot fully impersonate the part during initial onboarding within the owner's facility.
- Cannot extract field entropy after initial onboarding.
During initial onboarding, the owner is expected to instruct the device to program field entropy. Upon device reset, this results in a fresh LDevID. Attackers that have previously obtained UDS are not able to derive this LDevID. The owner is expected to register the new LDevID and subsequently validate all future DICE keys for the device against that LDevID.
When registering LDevID during device onboarding, the owner is expected to rely on IDevID as an authenticity signal over LDevID. It is assumed that the attacker has obtained UDS at this point, and therefore can themselves wield IDevID. Therefore, the authenticity signal granted by IDevID cannot be the only signal used to determine LDevID's trustworthiness. The owner's device onboarding flow must be resistant to remote person-in-the-middle attackers that may attempt to use a previously exfiltrated UDS to register a forged LDevID.
After an owner registers a device's LDevID as part of their device onboarding flow, and unless the device again passes through the owner's device onboarding flow, the owner should not trust IDevID to endorse any other LDevIDs.
This approach does not defend against supply-chain attackers that obtain fuse data for devices that enter the supply chain after their field entropy has been programmed, such as during RMA flows. The LDevID certificate also does not support revocation because there is no generic Caliptra OCSP service. Owners should either maintain an allowlist of LDevID certificates or revoke any of the upstream certificate authorities.
Owners are not required to program field entropy. Caliptra generates LDevID from the value of the field entropy fuses, which could be all zeroes or ones. Caliptra LDevID derivation descends from UDS so that LDevID properties are no worse than IDevID. Field entropy is expected to be stored in fuses to achieve an equivalent physical attack barrier to UDS.
It is the responsibility of the owner or the user to identify the certificate they wish to trust, and to potentially endorse with their own certificate authority: pCA, IDevID, LDevID, or AliasFMC.
FMC alias key
The LDevID CDI is mixed with a hash of FMC, as well as the security state of the device, via a FIPS-compliant HMAC, to produce CDIFMC. ROM uses CDIFMC to derive the AliasFMC keypair. ROM wields LDevID to issue a certificate for Alias. The AliasFMC certificate includes measurements of the security state and FMC. ROM makes CDIFMC, AliasFMC, and its certificate, available to FMC.
FMC mixes CDIFMC with a hash of runtime firmware to produce CDIRT. FMC uses CDIRT to derive the AliasRT alias keypair. FMC wields AliasFMC to issue a certificate for AliasRT. This alias certificate includes measurements of runtime firmware. FMC makes CDIRT, AliasRT, and its certificate, available to application firmware, while withholding CDIFMC and AliasFMC.
Security state
Devices may support features like debug unlock, DFT, or DFD flows that globally affect SoC state. These features, when enabled, significantly alter the security state of the device. The configuration of these features shall be captured in the device's DICE identity. The security state shall be captured as an input to the FMC's CDI, and represented within the FMC's alias certificate.
Owner authorization
Caliptra firmware shall be signed by the vendor. In addition, this firmware shall also be signed by an owner key. Caliptra must extract the owner's public key from the firmware image during cold boot, and latch the owner key into Caliptra's RAM for the remainder of its uptime3. Caliptra then uses both the vendor key and owner key to verify hitless firmware updates.
Caliptra shall attest to the value of the owner key, enabling external verifiers to ensure that the correct owner key was provisioned into the device. To perform this attestation, Caliptra includes the owner key as an input to the FMC's CDI (as part of "other attributes" from Figure 4 above), and represents it within the FMC's alias certificate.
The SoC may support a fuse bank for representing the hash of the owner's public key. If the SoC reports this value to Caliptra, Caliptra refuses to boot firmware unless the firmware was dual-signed by the key reported by SoC ROM's fuse registers.
The owner key, when represented in fuses or in the FMC's alias certificate, is a SHA384 hash of a structure that contains a list of owner public keys. This supports key rotation.
Provisioning UDS during Manufacturing (Subsystem Mode)
Note: In passive mode, SoC follows the same flows/restrictions as Caliptra 1.x

Figure 6: Subsystem Mode: UDS manufacturing flow
There are three ways of generating a UDS_SEED Use the internal TRNG to directly generate a 384-bit random number. Use an entity external to Caliptra such as an HSM or SoC-specific methodology to produce UDS-seed 384-bit random number that is pushed into the fuse controller (same as Caliptra 1.0). Combine the internal TRNG output with a Manufacturing time provided value to produce a 384-bit output.
UDS Manufacturing
- When SoC life cycle is in MANUFACTURING MODE, manufacturing service register bit [CPTRA_DBG_MANUF_SERVICE_REG[2]] is set to request for UDS seed programming flow.
- Caliptra ROM will sample this bit on power up; when this bit is set and Caliptra ROM rechecks that the life cycle state is manufacturing mode, it reads the iTRNG for a 512-bit value.
- Caliptra ROM writes the 512-bit value to the address available through a register named UDS_SEED_OFFSET which is strapped by SoC at integration time by using DMA HW assist macro available at ROMβs disposal.
- Caliptra ROM sets the corresponding status bit in CPTRA_DBG_MANUF_SERVICE_REG to indicate the flow completion.
- Manufacturing flow will poll/read this bit and then do the fuse burning flow as specified by the fuse controller spec and SoC specific VR methodologies (eg. Fuse macro voltage elevation flows etc.).
Provisioning IDevID during manufacturing

Figure 7: Passive Mode: Device manufacturing identity flow
- High Volume Manufacturing (HVM) programs the IDevID certificate attributes fuses. See IDevID Certificate for encodings.
- HVM programs NIST compliant UDS into fuses using SoC-specific fuse programming flow. Note that this UDS goes through an obfuscation function within Caliptra IP.
- SoC drives the security state, which indicates that it's a manufacturing flow. See Caliptra Security States for encodings.
- SoC (using a GPIO pin or SoC ROM) drives BootFSMBrk (this is also used for debug cases). This can be driven at any time before cptra_rst_b is deasserted.
- SoC follows the boot flow as defined in Caliptra IP HW boot flow to assert cptra_pwrgood and deassert cptra_rst_b, followed by writing to the fuse registers.
- HVM, through JTAG or using the Caliptra SoC interface, sets βCPTRA_DBG_MANUF_SERVICE_REGβ bit 0 to request a CSR.
- HVM, through JTAG or using the Caliptra SoC interface, writes to βCPTRA_BOOTFSM_GOβ to allow Caliptraβs internal BootFSM to continue to bring up microcontroller out of reset.
- ROM reads the manufacturing state encoding from the βCPTRA_DBG_MANUF_SERVICE_REGβ register, acquires the mailbox lock, and populates the Caliptra internal SRAM (the mailbox SRAM hardware structure is reused) with the CSR.
- HVM, through JTAG or using the SoC interface, polls for the βIDevID CSR ready" bit 24 that is set in βCPTRA_FLOW_STATUSβ register.
- HVM reads mbox_status[3:0] to check if the data is ready to be read (DATA_READY encoding).
- HVM must clear bit 0 of CPTRA_DBG_MANUF_SERVICE_REG, indicating that it completed reading the CSR.
- Caliptra ROM opens the Caliptra Mailbox for SoC usages, such as FW loading (if required in some HVM flows). The SoC is only allowed to request a lock of the AXI-exposed mailbox interface after this CSR operation is complete.
Certificate format
Caliptra certificates follow the X.509 v3 format described in RFC 5280. After vendor provisioning, Caliptra's certificate chain contains the following certificates:
- Provisioner CA (may be one or more certificates)
- IDevID
- LDevID
- AliasFMC
- AliasRT
- DPE
After owner provisioning, an Owner CA may endorse the IDevID, LDevID, or AliasFMC public keys. Owner CA provisioning is outside the scope of this specification.
Caliptra generates the LDevID, AliasFMC, AliasRT, and DPE certificates. The vendor, and optionally the owner, generate all other certificates.
Serial number algorithm
Caliptra uses certificate templates to avoid implementing fully capable X.509 v3 parsers and generators. Templates require certificates to be fixed length. This imposes constraints on the certificate serial numbers:
- Positive integer
- First octet as non-zero
- 20 octets in length
All Caliptra certificate serial numbers are generated with the following algorithm. The input is the certificate ECDSA or PQC MLDSA-87 public key in uncompressed form:
- Convert to DER format.
- Compute SHA256 digest.
- AND the least-significant-byte with ~0x80.
- OR the least-significant-byte with 0x04.
- Perform byte-wise copies of the least-significant 20 bytes into the certificate template.
Provisioner CA
Provisioner CA (pCA) is a set of one or more certificates issued by the vendor. The vendor is responsible for provisioning pCA to the SoC. Caliptra does not consume pCA. See Reference 5 for guidance on pCA.
IDevID certificate
The vendor issues the IDevID certificate during SoC manufacturing. As part of provisioning IDevID during manufacturing, Caliptra uses the UDS to derive the IDevID key pair and generate a CSR. The vendor's pCA uses the CSR to generate and sign the IDevID certificate. The CSR uses the format defined in PKCS#10.
For IDevID to endorse LDevID, Caliptra requires the vendor to implement an X.509 v3 IDevID certificate described in RFC 5280 with the field values specified in Table 7: IDevID certificate fields. The vendor shall also populate all extensions from the "Requested Extensions" attribute in the CSR. It is also recommended that the vendor add the Authority Information Access (AIA) extension to the IDevID certificate and maintain an Online Certificate Status Protocol (OCSP) responder with a URL pointed to by the AIA extension.
Table 7: IDevID certificate fields
| Field | Sub field | Value |
|---|---|---|
| Version | v3 | 2 |
| Serial Number | - | Generate with serial number algorithm using IDevID public key in uncompressed form |
| Validity | notAfter | 99991231235959Z |
| Subject Name | CN | Caliptra 1.0 IDevID |
| serialNumber | Hex-encoded printable string of SHA256 hash of DER-formatted IDevID public key in uncompressed form | |
| Subject Public Key Info | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Public Key | IDevID Public Key value | |
| Signature Algorithm Identifier | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Signature Value | - | Digital signature for the certificate |
| KeyUsage | keyCertSign | 1 |
| Basic Constraints | CA | TRUE |
| pathLen | 5 | |
| Subject Key Identifier | - | specified by IDevID attribute fuses |
| tcg-dice-Ueid | ueid | UEID specified by IDevID attribute fuses |
Caliptra does not consume the IDevID certificate. Caliptra needs attributes of the IDevID certificate in order to generate the Authority Key Identifier extension for the LDevID and to populate the TCG Universal Entity ID (UEID) extension for Caliptra-generated certificates. The vendor must fuse these attributes into the IDevID attribute fuses for Caliptra to consume. The encoding of these attribute fuses is as follows:
- Flags (byte 0, bits [1:0]): Key ID algorithm for IDevID Subject Key Identifier.
- 0 = SHA1 of DER-formatted IDevID public key in uncompressed form
- 1 = First 20 bytes of SHA256 of DER-formatted IDevID public key in uncompressed form
- 2 = First 20 bytes of SHA384 of DER-formatted IDevID public key in uncompressed form
- 3 = raw
- Reserved (bytes 1 to 3)
- Subject Key ID (bytes 4 to 23): if Flags = 3, the IDevID Subject Key Identifier to use as the LDevID Authority Key Identifier.
- UEID type (byte 24): UEID type as defined in IETF RATS specification. Used for TCG UEID extension.
- Reserved (bytes 25 to 27)
- Manufacturer Serial Number (bytes 28 to 43): the 128-bit unique serial number of the device to be used for the TCG UEID extension in the Caliptra-generated LDevID, AliasFMC, and AliasRT certificates.
The IDevID certificate is unique for each device and non-renewable. The SoC must be able to retrieve the IDevID certificate at runtime. To save flash space and aid in recoverability, it is recommended that the vendor define an IDevID certificate template such that the SoC at runtime can reconstruct the same certificate that the pCA endorsed. The SoC is recommended to store the IDevID certificate signature in fuses and the IDevID certificate template in the firmware image. Caliptra runtime firmware provides APIs to aid in reconstructing the certificate:
- GET_IDEV_INFO to return the IDevID public key.
- GET_IDEV_CERT to return the certificate given a to-be-signed (TBS) payload and the certificate signature from fuses. The TBS is the certificate template already patched with the IDevID public key, Subject Key Identifier, serial number, and any extensions that are unique to the device that the vendor may have included.
Caliptra does not allocate fuses in its fuse map for the IDevID certificate signature. Caliptra allocates "IDEVID MANUF HSM IDENTIFIER" fuses that the vendor can use to aid certificate reconstruction.
LDevID certificate
Caliptra ROM generates the LDevID certificate and endorses it with the IDevID private key. The LDevID certificate implements the following field values:
Table 8: LDevID certificate fields
| Field | Sub field | Value |
|---|---|---|
| Version | v3 | 2 |
| Serial Number | - | Generate with serial number algorithm using LDevID public key in uncompressed form |
| Issuer Name | CN | Caliptra 1.0 IDevID |
| serialNumber | Hex-encoded printable string of SHA256 hash of DER-formatted IDevID public key in uncompressed form | |
| Validity | notBefore | 20230101000000Z |
| notAfter | 99991231235959Z | |
| Subject Name | CN | Caliptra 1.0 LDevID |
| serialNumber | Hex-encoded printable string of SHA256 hash of DER-formatted LDevID public key in uncompressed form | |
| Subject Public Key Info | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Public Key | LDevID Public Key value | |
| Signature Algorithm Identifier | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Signature Value | - | Digital signature for the certificate |
| KeyUsage | keyCertSign | 1 |
| Basic Constraints | CA | True |
| pathLen | 4 | |
| Subject Key Identifier | - | First 20 bytes of SHA256 hash of DER-formatted LDevID public key in uncompressed form |
| Authority Key Identifier | - | specified by IDevID attribute fuses |
| tcg-dice-Ueid | ueid | UEID specified by IDevID attribute fuses |
Caliptra does not generate an LDevID CSR. Owners that wish to endorse LDevID must do so with proprietary flows.
AliasFMC certificate
Caliptra ROM generates the AliasFMC certificate and endorses it with the LDevID private key. The AliasFMC certificate implements the following field values:
Table 9: AliasFMC certificate fields
| Field | Sub field | Value |
|---|---|---|
| Version | v3 | 2 |
| Serial Number | - | Generate with serial number algorithm using FMC Alias public key in uncompressed form |
| Issuer Name | CN | Caliptra 1.0 LDevID |
| serialNumber | Hex-encoded printable string of SHA256 hash of DER-formatted LDevID public key in uncompressed form | |
| Validity | notBefore | notBefore from firmware manifest |
| notAfter | notAfter from firmware manifest | |
| Subject Name | CN | Caliptra 1.0 FMC Alias |
| serialNumber | Hex-encoded printable string of SHA256 hash of DER-formatted FMC Alias public key in uncompressed form | |
| Subject Public Key Info | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Public Key | FMC Alias Public Key value | |
| Signature Algorithm Identifier | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Signature Value | - | Digital signature for the certificate |
| KeyUsage | keyCertSign | 1 |
| Basic Constraints | CA | True |
| pathLen | 3 | |
| Subject Key Identifier | - | First 20 bytes of SHA256 hash of DER-formatted FMC Alias public key in uncompressed form |
| Authority Key Identifier | - | First 20 bytes of SHA256 hash of DER-formatted LDevID public key in uncompressed form |
| tcg-dice-Ueid | ueid | UEID specified by IDevID attribute fuses |
| tcg-dice-MultiTcbInfo | Flags | NOT_CONFIGURED if lifecycle is unprovisioned |
| NOT_SECURE if lifecycle is manufacturing | ||
| DEBUG if not debug locked | ||
| SVN | [0] fuse SVN | |
| [1] firmware SVN | ||
| FWIDs | [0] SHA384 digest of | |
| lifecycle state | ||
| debug locked state | ||
| anti-rollback disable fuse | ||
| ECDSA vendor public key index fuse | ||
| LMS vendor public key index fuse | ||
| LMS verification enable fuse | ||
| boolean indicating whether owner public key hash is in fuses | ||
| vendor public key hash | ||
| owner public key hash | ||
| [1] SHA384 digest of FMC |
Caliptra does not generate an AliasFMC CSR. Owners that wish to endorse AliasFMC must do so with proprietary flows.
AliasRT certificate
Caliptra FMC generates the AliasRT certificate and endorses it with the AliasFMC private key. The AliasRT certificate implements the following field values:
Table 10: AliasRT certificate fields
| Field | Sub field | Value |
|---|---|---|
| Version | v3 | 2 |
| Serial Number | - | Generate with serial number algorithm using RT Alias public key in uncompressed form |
| Issuer Name | CN | Caliptra 1.0 FMC Alias |
| serialNumber | Hex-encoded printable string of SHA256 hash of DER-formatted FMC Alias public key in uncompressed form | |
| Validity | notBefore | notBefore from firmware manifest |
| notAfter | notAfter from firmware manifest | |
| Subject Name | CN | Caliptra 1.0 Rt Alias |
| serialNumber | Hex-encoded printable string of SHA256 hash of DER-formatted RT Alias public key in uncompressed form | |
| Subject Public Key Info | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Public Key | RT Alias Public Key value | |
| Signature Algorithm Identifier | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Signature Value | - | Digital signature for the certificate |
| KeyUsage | keyCertSign | 1 |
| Basic Constraints | CA | True |
| pathLen | 2 | |
| Subject Key Identifier | - | First 20 bytes of SHA256 hash of DER-formatted RT Alias public key in uncompressed form |
| Authority Key Identifier | - | First 20 bytes of SHA256 hash of DER-formatted FMC Alias public key in uncompressed form |
| tcg-dice-Ueid | ueid | UEID specified by IDevID attribute fuses |
| tcg-dice-TcbInfo | SVN | Firmware SVN |
| FWIDs | [0] SHA384 digest of RT |
Caliptra does not generate an AliasRT CSR. Owners that wish to endorse AliasRT must do so with proprietary flows.
DPE certificate
Caliptra RT generates the DPE certificate and endorses it with the AliasRT private key. The DPE certificate fields are described in the Caliptra Runtime specification. DPE also supports issuance of CSRs.
Caliptra security states

Figure 6: Caliptra security states
Definitions
- Non-Debug: Caliptra JTAG is not open for microcontroller and HW debug. Alias: DebugLocked
- Debug: Caliptra JTAG is open for microcontroller and HW debug. Alias: DebugUnlocked
- Unprovisioned: Blank/unprogrammed fuse part.
- Manufacturing: Device is in the manufacturing flow where HVM Caliptra fuses are programmed.
- Production: All of Caliptraβs HVM fuses are programmed.
- Secure: Any security state that restricts access to Caliptra from an external entity and has a known, specified function in the device lifecycle. Requires DebugLocked and either the Manufacturing or Production configuration.
- Insecure: Any security state other than those deemed secure. This includes all undefined states, all states with DebugUnlocked, and the Unprovisioned state. In accordance with the threat model, these states are considered a potential risk due to attacks via DFT or DFD channels, exploitation of unforeseen logic issues, or undefined behavior. These insecure states necessitate flushing of Caliptra secrets.
Notes:
- Caliptraβs security state is determined by the SoCβs security state and the SoC device lifecycle state.
- Caliptraβs state is considered a mode of operation.
- Caliptra security state is defined by the uppermost bit of the encoding below; 1=DebugLocked and 0=DebugUnlocked.
- Lower 2 bits are mapped to device lifecycle (unprovisioned, manufacturing, production).
- SoCβs security state may also be influenced by its own device lifecycle. A HW state machine must drive the SoC security state.
- Caliptraβs security state determines Caliptraβs debug state and the state of its security assets.
- In general, if Caliptra is in an insecure state, all keys and assets are βzeroizedβ. Zeroized may mean switching to all 0s, 1s, or debug keys based on the key. See Caliptra Assets for information.
Table 11: Security states
| Security state, device lifecycle state [2:0] | State | Definition | State transition requirement |
|---|---|---|---|
| 000b | DebugUnlocked and unprovisioned | This shall be the default state value for Caliptraβs security state; it is used for development and early Caliptra bring up. This state is not used to provision the Caliptra assets. In this state: - UDS and all other identity critical assets shall not be programmed in fuses. Un-programmed fuse bits shall be read as 0s (zero). The debug UDS shall be obfuscated and de-obfuscated using the debug obfuscation key. - Obfuscation key: The debug obfuscation key shall be used. - Caliptra JTAG is unlocked and allows microcontroller debug. - Caliptra JTAG can access IP internal registers through FW. | Unprovisioned to any other state requires a cold boot of Caliptra and SoC. |
| 101b | DebugLocked and manufacturing | Caliptra must be placed in this state during the secure HVM process. In this state: - UDS and other identity critical assets shall be programmed into fuses. They are written into Caliptra fuse registers, similar to the βSecureβ state. - All security assets shall be in production mode (production UDS and obfuscation shall be used). - Upon pwrgood assertion, Caliptra JTAG shall be locked; microcontroller debug shall be disabled. - Caliptra microcontroller can be interrupted through JTAG mailbox. | Manufacturing -> insecure state transition is allowed with warm reset and Caliptra clears all of the security critical assets and registers before JTAG is opened. Manufacturing -> secured state is allowed ONLY with a cold boot. See Provisioning During Manufacturing for details. |
| 111b | DebugLocked and production | All security assets are in production mode. In this state: - Production UDS and obfuscation key shall be used. - CPU execution shall be enabled. - All βbackdoorβ functionality shall be disabled (for example, developer functions and functionality that could reveal sensitive information or result in escalation of privileges). - Debug functions shall be disabled. Caliptra JTAG is locked β microcontroller debug shall be disabled. Caliptra microcontroller shall not be interruptible through JTAG mailbox. - DFT functions shall be disabled. | DebugLocked -> DebugUnlocked is possible without cold boot and Caliptra clears all of the security critical assets and registers before JTAG is opened. |
| 011b | DebugUnlocked and production | This state is used when debugging of Caliptra is required. When in this state: UDS and other identity critical assets are programmed into fuses. They may not have been written into Caliptra fuse registers if the insecure state entered before Caliptra is out of reset. If the insecure state transition happened after fuses are written to Caliptra, they are cleared when the security state transitions from secure/production -> insecure. Caliptra state: All security assets are in debug mode (UDS and obfuscation key are in production state). - UDS: Reverts to a βwell-knownβ debug value. - Obfuscation key: Switched to debug key. - Key Vault is also cleared. - Caliptra JTAG is unlocked and allows microcontroller debug. - Caliptra JTAG can access IP internal registers through FW or directly. | DebugUnlocked -> DebugLocked is allowed ONLY with a cold boot. |
Notes:
- End-of-life state is owned by SoC. In end-of-life device lifecycle state, Caliptra shall not not be brought out of reset.
- Other encodings are reserved and always assumed to be in a secure state.
Each of these security states may be mapped to different SoC level debug and security states. SoCβs requirement is that if the SoC enters a debug state, then Caliptra must also be in an insecure state where all assets are cleared. Caliptra security state is captured by hardware on every warm reset; therefore SoC integrators enforce the security state transition policies for cold boot events. These policies are described in the preceding table.
Service surface
The service surface of Caliptra has multiple vectors. All use cases are control plane services, useful to power on a system or start a task. Supporting line rate high performance IO cryptography or any other data path capability is not required.
- Logic IOs: Required to indicate status of the IP, availability of a message through AXI, and to enable or disable certain debug capabilities (like JTAG enable or disable).
- Command mailbox: Caliptra shall offer services to other parts of the SoC. The APIs are documented in the Caliptra Runtime specification and summarized below:
- Loading firmware: Caliptra firmware is loaded via the mailbox at cold boot. In addition, Caliptra firmware can be loaded at runtime to support hitless or impactless updates.
- DICE-as-a-Service: Caliptra shall expose the TCG DICE Protection Environment iRoT Profile API, allowing Caliptra to derive and wield a DICE identity on behalf of other elements within the SoC. For example, Caliptra can sign messages for an SPDM responder.
- Measurement Vault: Caliptra shall support stashing of measurements for the code and configuration of the SoC. Caliptra can provide these measurements via PCR Quote API or via DPE.
- FW Authentication: Caliptra supports ECDSA and PQC MLDSA-87 verification for SoC firmware beyond its own. The SHA384 block exposes a HW API for hashing firmware. The runtime firmware exposes an ECDSA and MLDSA-87 verification API that uses the hash computed by the SHA384 block.
Device resilience
As noted earlier, Caliptra plays a role in maintaining the resilience posture of the SoC as defined by NIST SP 800-193 Platform Firmware Resiliency Guidelines (see Reference 1). As the Silicon RTM and RTI, Caliptra is either responsible for, or participates in, various protection and detection requirements described in the NIST publication.
The following table describes the NIST SP 800-193 requirements that Caliptra shall meet, either on its own or in conjunction with other components within the SoC or platform. Requirements not listed are assumed to be not covered and out-of-scope for Caliptra. In particular, most requirements related to firmware update and recovery are out-of-scope and must be handled by other components of the system.
Table 12: NIST SP 800-193 requirements
| NIST SP 800-193 Chapter | Requirement | Caliptra responsibility |
|---|---|---|
| 4.1.1 | All security mechanisms and functions shall be founded to Roots of Trust (RoT). | Caliptra forms the basis for all trust in the SoC starting from execution of its immutable ROM. See the Secure Boot Flow section. |
| 4.1.1 | If Chains of Trust (CoT) are used, RoT shall serve as the anchor for the CoT. | Caliptra firmware shall be authenticated and executed as part of a Chain of Trust extended from the Caliptra ROM, while all other firmware shall be measured into a CoT extended from the Caliptra ROM. See the Secure Boot Flow section. |
| 4.1.1 | All RoTs and CoTs shall either be immutable or protected using mechanisms that ensure all RoTs and CoTs remain in a state of integrity. | Caliptra firmware is authenticated and executed as part of a Chain of Trust extended from the Caliptra ROM. See the Secure Boot Flow section. The SoC or platform is responsible for maintaining integrity for other elements of the CoT. |
| 4.1.1 | All elements of the CoT for update, detection, and recovery in non-volatile storage shall be implemented in platform firmware. | Caliptra forms the basis for RTM, which the SoC can use to create detection capabilities. All other silicon RoT capabilities are extended by additional firmware loaded in the SoC and anchored by Caliptra. |
| 4.1.1 | The functions of the RoTs or CoTs shall be resistant to any tampering attempted by software running under, or as part of, the operating system on the host processor. | Caliptra shall run on a dedicated microcontroller, isolated physically from access by other components in the system. |
| 4.1.1 | Information transferred from the software on the host processor to the platform firmware shall be treated as untrusted. | Caliptra shall verify the authenticity of its firmware using an approved digital signature verification mechanism. |
| 4.1.1 | CoTs may be extended to include elements that are not from non-volatile storage. Before use, those elements shall be cryptographically verified by an earlier element of the CoT. | Caliptra shall verify the authenticity of its firmware using an approved digital signature verification mechanism. Caliptra shall also collect the measurement of the SoC security processor FMC code before it is verified and executed by the SoC. |
| 4.1.2 | If the key store is updateable, then the key store shall be updated using an authenticated update mechanism, absent unambiguous physical presence through a secure local update. | Hashes for the keys used to authenticate Caliptra FW are programmed into fuses during manufacturing. If a key is deemed to be compromised, that key may be revoked and the next key used instead. |
| 4.1.3 | Each platform device that implements a detection capability shall rely on either a Root of Trust for Detection (RTD), or a Chain of Trust for Detection (CTD). The CTD is anchored by an RTD for its detection. | Caliptra forms the basis for RTM, which the SoC can use to create detection capabilities. Caliptra firmware shall be authenticated and executed as part of a Chain of Trust extended from the Caliptra ROM, while all other firmware shall be measured into a CoT extended from the Caliptra ROM. See the Secure Boot Flow section. |
| 4.1.3 | The RTD or CTD shall include or have access to information necessary to detect corruption of firmware code and critical data. | Caliptra relies on hashes of authorized keys stored in fuses. Those hashes are then checked against public keys found in firmware headers to authenticate Caliptraβs runtime firmware. Caliptra relies on redundancy in the fuses to protect the key and configuration data. |
| 4.2.3 | If critical platform firmware code in non-volatile memory is copied into RAM to be executed (for performance, or for other reasons) then the firmware program in RAM shall be protected from modification by software or shall complete its function before software starts. | Caliptra shall run on a dedicated microcontroller, isolated physically from access by other components in the system. |
| 4.2.3 | If critical platform firmware uses RAM for temporary data storage, then this memory shall be protected from software running on the platform until the dataβs use is complete. | Caliptra shall run on a dedicated microcontroller, isolated physically from access by other components in the system. |
| 4.2.3 | Software shall not be able to interfere with the intended function of critical platform firmware. For example, by denying execution, modifying the processor mode, or polluting caches. | Caliptra shall run on a dedicated microcontroller, isolated physically from access by other components in the system. In addition, the Caliptra subsystem begins execution before other firmware is allowed to run. |
| 4.2.4 | Critical data shall be modifiable only through the device itself or defined interfaces provided by device firmware. Examples of defined interfaces include proprietary or public application programming interfaces (APIs) used by the deviceβs firmware, or standards-based interfaces. Symbiont devices may rely on their host devices to meet this requirement. | Caliptra receives firmware and configuration input only via defined interfaces within the SoC. See the Mailbox section. |
| 4.2.1.3 | The authenticated update mechanism shall be capable of preventing unauthorized updates of the device firmware to an earlier authentic version that has a security weakness or would enable updates to a version with a known security weakness. | Caliptra supports a mechanism for detecting and preventing execution of a prior firmware image that is no longer authorized. See the Anti-rollback Support section. |
| 4.3.1 | A successful attack that corrupts the active critical data or the firmware image, or subverts their protection mechanisms, shall not in and of itself result in a successful attack on the RTD or the information necessary to detect corruption of the firmware image. | Caliptra shall verify the signature of any firmware it loads during each boot. If the signature verification fails, Caliptra shall notify the SoC that firmware recovery must be performed. See the Error Reporting and Handling section. |
| 4.3.1 | Verify integrity, using an approved digital signature algorithm or cryptographic hash, of device firmware code prior to execution of code outside the RTD. | Caliptra shall perform digital signature verification of its firmware before it is allowed to execute. |
| 4.3.1 | If firmware corruption is detected, the RTD or CTD should be capable of starting a recovery process to restore the device firmware code back to an authentic version. | Caliptra shall notify the SoC via the Mailbox interface to initiate the recovery process. |
| 4.3.1 | The detection mechanism should be capable of creating notifications of firmware corruption. | Caliptra shall notify the SoC via the Mailbox interface to initiate the recovery process. |
| 4.3.1 | The detection mechanism should be capable of logging events when firmware corruption is detected. | It is the responsibility of the SoC to log any corruption events upon notification by Caliptra. |
| 4.3.2 | The RTD or CTD shall perform integrity checks on the critical data prior to use. Integrity checks may take the form, for example, of validating the data against known valid values or verifying the hash of the data storage. | Caliptra relies on SoC fuse integrity to store its configuration data, which is owned and passed to Caliptra through the Mailbox. |
| 4.3.2 | The RTD or CTD should be capable of creating notifications of data corruption. | See the Error Reporting and Handling section. |
| 4.3.2 | The detection mechanism should be capable of logging events when data corruption is detected. | It is the responsibility of the SoC to log any corruption events upon notification by Caliptra. |
Secure boot flow
Caliptra shall follow and implement the secure boot guidelines as described in Reference 3.
For the detailed flow, see the hardware section and firmware verifcation section.
Hitless update
A βhitlessβ (aka βimpactlessβ) update occurs when an update is applied to Caliptraβs executing firmware without requiring a SoC or machine reboot. A hitless update allows Caliptra FW4 to remain up to date with FW security and/or functional patches while preventing or reducing machine downtime. Hitless update shall take effect immediately upon application (post-cryptographic verification). Updates to the machineβs persistent storage are still required because they ensure that Caliptra reboots to the latest FW if the system requires a restart or reboot.
Caliptra contains multiple hardware isolated registers for Platform Configuration Registers (PCR). These PCRs serve as volatile storage for concise cryptographic measurement of security state, including Caliptraβs own firmware.
Journey measurements matter because hitless updates are a challenge for devices that only capture their current firmware version and state. This is particularly true when the previous state may have impacted the current state of dependent components within the SoC. For example, a device might move from firmware version A to firmware version B without assuming a clean start to flush state. Vulnerabilities in firmware version A might impact version B. Preserving a device boot measurement and currently running measurement can highlight differences, but preserving these measurements does not distinguish between transitional states, such as when intermediate updates have the potential to expose the device to vulnerabilities. For example, a device may move from firmware version A, to B, and then to C without a restart, whereas another device of the same type might transition from A to C without transitioning through B. If tracking only the boot and current firmware version, should a vulnerability be found in version B, it is impossible to identify which devices transitioned through B compared to devices that transitioned from A to C directly.
To capture all firmware and configuration changes, Caliptra tracks and attests to both current measurements and cumulative measurement PCR banks. The current measurement is a snapshot of the currently running firmware and configuration. This provides easy reference for the current version. If the current and cumulative measurements are different, it can safely be assumed that the device has undergone some update. The cumulative measurement captures all of the firmware and state transitions from a clean cold boot to the current version. The cumulative measurement must be accompanied by a log structure that describes the path from boot to current measurement using hash extensions. A verifier can understand a deviceβs path to its current state by replaying log entries to reconstruct the cumulative measurement.
The log and cumulative measurement mechanism is similar to that used in TPM. In this model, Caliptra only needs to securely manage the measurements; the log does not need to be secured or maintained by Caliptra or the SoC. The construction of measurements through cryptographic hash extensions means that the log must provide the exact order and evidence needed to reconstruct the measurement. As such, the log is tamper evident by design and does not need to be kept secure.
Caliptra contains 32 384-bit PCR banks that are extendable by the SHA engine, and readable by Caliptra firmware. The usage of the PCR banks is as follows:
Table 13: PCR bank usage
| PCR number | Type | Extend control | Description |
|---|---|---|---|
| PCR0 | Current | ROM | Holds Caliptraβs FMC measurement and ROM policy configuration. |
| PCR1 | Cumulative | ROM | Holds journey of Caliptraβs FMC measurement and ROM policy configuration. |
| PCR2 | Current | FMC | Holds Caliptra's runtime firmware and firmware manifest measurements. |
| PCR3 | Cumulative | FMC | Holds journey of Caliptra's runtime firmware and firmware manifest measurements. |
| PCR4 to PCR30 | - | RT | Holds measurements extended by EXTEND_PCR commands (serviced by RT). |
| PCR31 | Cumulative | ROM | Holds measurements extended by STASH_MEASUREMENTS commands (serviced by both ROM and RT). |
For PCR0 and PCR1, ROM issues the following extend operations in order:
- An array containing the following fields as 8-bit values:
- Lifecycle state
- Debug locked state
- Anti-rollback disable fuse
- ECDSA vendor public key index
- MLDSA-87 vendor public key index
- Cold-boot firmware SVN
- Effective Fuse SVN (i.e., 0 if anti-rollback disable is set)
- LMS vendor public key index
- LMS verification enable fuse
- Boolean indicating whether the owner public key hash is in fuses
- Vendor public key hash
- Owner public key hash
- Digest of FMC
Caliptra ROM fails to boot if the following values do not remain constant across a hitless update:
- Owner public key hash
- ECDSA vendor public key index
- MLDSA-87 vendor public key index
- LMS vendor public key index
- FMC digest
Attestation of Caliptra's update journey
Upon every cold boot and hitless update, Caliptra ROM extends Caliptra's FMC measurement and ROM policy configuration into PCR0 (current) and PCR1 (cumulative). Upon every cold boot and hitless update, Caliptra FMC extends Caliptra's runtime firmware and firmware manifest measurements into PCR2 (current) and PCR3 (cumulative). The current measurement of the FMC, ROM policy configuration, RT, and FW manifest are used to derive a CDI and an alias key given to runtime firmware. FMC places runtime firmware's measurements into runtime firmware's alias key certificate, and signs that certificate with FMC's alias key.
When runtime firmware boots following a hitless update, it will use the following pieces of data for building the DPE nodes and certificate chain:
- Its CDI, derived by FMC
- Its alias key, generated by FMC
- Its alias certificate, signed by FMC
- Its journey measurements in PCR3, extended by FMC
- SRAM state, established by the prior runtime firmware image
SRAM state consists of measurements captured by prior runtime firmware images, and does not contain secrets or executable data5. Therefore, the trustworthiness of runtime firmware is reflected in the measurements captured by FMC and is evident in the runtime firmware alias certificate.
Caliptra firmware will attest to PCR3 by including it as an input in all DPE leaf key derivations and as evidence in all DPE leaf key certificates.
Commentary: recovery from a bad hitless update
Suppose the following Caliptra firmware images exist:
- Version A: presumed-good
- Version B: known-bad
- Version C: presumed-good
A remote verifier wishes to confirm that a given Caliptra device has not run version B since cold boot. The remote verifier can challenge the SoC with a freshness nonce; higher-layer software passes that freshness nonce as a request to Caliptra's DPE for signing. The remote verifier receives the following pieces of evidence:
- An endorsement over LDevID, which may take a number of forms, including:
- Caliptra's vendor-signed certificate over IDevID, and an IDevID-signed certificate over LDevID.
- An owner-signed certificate over IDevID, and an IDevID-signed certificate over LDevID.
- An owner-signed certificate over LDevID.
- An LDevID-signed certificate over AliasFMC, which includes FMC's measurements as captured by ROM.
- An AliasFMC-signed certificate over AliasRT, which includes runtime firmware's measurements as captured by FMC.
- An AliasRT-signed certificate over a leaf DPE key, which includes PCR3 as read by runtime firmware, along with other measurements previously stashed in SRAM.
- A log structure6 that represents the measurements that have been extended into PCR3.
- A leaf-DPE-signed blob7 containing the freshness nonce.
The remote verifier evaluates (1) according to ownership policies to determine whether the Caliptra device is trustworthy, before proceeding to verify the rest of the attestation response.
Thus satisfied in the trustworthiness of the Caliptra device, the remote verifier can then evaluate the trustworthiness of FMC by inspecting the measurements in (2), AliasFMC's certificate. The verifier can reject the attestation if those measurements do not conform to a known-good value.
Thus satisfied in the trustworthiness of FMC, the remote verifier can then evaluate the trustworthiness of runtime firmware by inspecting the measurements in (3), AliasRT's certificate. If version A or C is running, then the PCR3 measurement present in (4), the leaf DPE key certificate, is presumed to be an honest reflection of the hardware register as read by runtime firmware. The verifier can reject the attestation if AliasRT's certificate indicates that version B is currently running.
Thus satisfied that version B is not currently running and that PCR3 is an accurate reflection of the hardware register, the remote verifier can then compare the log in (5) to PCR3 in (4) to confirm its authenticity, then walk the log to confirm that FMC never launched version B since cold-boot. If version B did run, that firmware could have maliciously modified DPE measurements stashed in SRAM, but could not have modified the contents of PCR3 to erase the evidence that version B ran at all, and could not have influenced the behavior of firmware versions A or C to prevent them from accurately reporting the contents of PCR3.
Thus satisfied that version B has not run since power-on, the verifier can also optionally inspect other measurements in (4) to evaluate the journey of other SoC components, whose measurements were previously stored within Caliptra's SRAM.
Finally, the verifier can confirm freshness by comparing the nonce in (6) to the one emitted in the original challenge. Because DPE only allows a derived leaf key to be used if the measurements present in its leaf certificate are a reflection of the current state, the fact that the freshness nonce was signed by DPE is evidence that the measurements in (4) are fresh.
Commentary: maintaining sealed secrets across a power cycle
Caliptra does not seal secrets directly. However, Caliptra does implement DPE, which allows secrets to be sealed to an external sealer such as a TPM, with a policy that only allows those secrets to be unsealed if Caliptra allows the host to wield a particular leaf DPE key. This leaf DPE key is permuted based on the hitless update journey of the various components whose measurements are stored within Caliptra.
This poses a challenge for maintaining sealed secrets across a power cycle. Suppose the SoC cold booted CPU microcode A, then hitlessly updated to B, and then to C. The measurements stored within Caliptra's SRAM will represent the [A->B->C] update journey, and DPE's leaf key is derived based on this journey.
Let us assume that upon each hitless update, the firmware update is also written to persistent storage, such that on the next cold boot the new firmware will run. Thus, on the next boot, the SoC's microcode update journey is simply [C]. This is a different journey than [A->B->C], and so DPE's leaf key will differ. The old secret sealed to the old DPE leaf key is no longer accessible to the SoC.
To anticipate this eventuality, before a power cycle, the SoC can instruct DPE to predict what the DPE leaf public key will be if the microcode journey is simply [C], using DPE's simulation context. The SoC can then reseal secrets to the external sealer with a policy that can be satisfied if the computed public key is used.
Note: as all DPE leaf keys are derived using Caliptra runtime firmware's CDI, a DPE simulation context cannot predict the leaf key that would be available if a different Caliptra firmware image were to boot (because one Caliptra firmware image does not have access to a different image's CDI). Therefore, if a different Caliptra firmware image is staged to persistent storage, Caliptra must first be hitlessly updated to that image before a simulation context can be used to predict public keys that will be available to that image on the next cold boot.
Attestation of SoC update journey
Caliptra shall also attest to the journeys of SoC components. A SoC component's journey may change independently of other components. For example, SoC components may implement partial resets or hitless updates that cause the component's firmware or configuration to reload.
Caliptra shall maintain a reboot counter for each component. Caliptra shall increment the reboot counter and update the journey measurement for calls that indicate that the component's state changed. Caliptra shall attest the journey measurement and report the counter value on-demand. The verifier is assumed to have knowledge of update events at an associated reboot counter (via an event log) but not have knowledge of reset events. The verifier can compute the journey measurement via multiplicatively extending the current measurement by the reset counter. For example:
- Upon cold boot, SoC component boots firmware A. Reboot counter is 0. The tuple of (0,A) is in the event log.
- SoC component is partial reset once. Reboot counter is 1.
- SoC component is hitlessly updated to firmware B. Reboot counter is 2. The tuple of (2,B) is in the event log.
- SoC component is partial reset twice. Reboot counter is 4.
The corresponding journey measurement computation is the chained extension of [A->A->B->B->B]. The verifier can ascertain this through the two event log entries.
Anti-rollback support
Caliptra shall provide fuse banks (refer to Table 21: Caliptra Fuse Map) that are used for storing monotonic counters to provide anti-rollback enforcement for Caliptra mutable firmware. Each distinctly signed boot stage shall be associated with its own anti-rollback fuse field. Together with the vendor, Caliptra allows owners to enforce strong anti-rollback requirements, in addition to supporting rollback to a previous firmware version. This is a critical capability for hyperscalers.
Caliptra firmware shall include an SVN value in the signed header. If the firmware's SVN value is less than the current counter value in the fuse bank, Caliptra shall refuse to boot that firmware, regardless of whether the signature is valid.
Alternatively, platform vendors may prefer to manage firmware storage and rollback protection in a different manner, such as through a dedicated Platform RoT. In such cases, the vendor may wish to disable anti-rollback support from Caliptra entirely. This disable support is available via an OTP/fuse setting.
Each of Caliptra's internal anti-rollback fuse banks shall support a minimum counter value of 64. This feature is expected to be used sparingly.
Physical attack countermeasures
Caliptra shall implement countermeasures designed to deter both glitching (also referred to fault-injection (FI)) and side-channel attacks (simple power analysis (SPA) and differential power analysis (DPA)).
The Caliptra threat model guides the priority of which physical countermeasures are based on a specific physical implementation.
From the top, an adversary in the supply chain has essentially unlimited time to glitch the chip and make it reveal any private key material or symmetric secrets. One Glitch To Rule Them All is one example with recency bias. The most critical countermeasures must prevent non-destructive extraction of those secrets. Otherwise, an adversary who succeeds can silently impersonate production-serving assets at a later time.
Randomly generated per-part entropy is subject to physical inspection attacks in the supply chain as well. The fuses that store the UDS entropy shall be protected to a degree that forces an attacker to perform a destructive operation to read their values. Decapping and fibbing attacks should at least penetrate enough layers and metal shielding to render the part useless, if not being outright impossible to carry out. Entropy tied to a damaged asset typically requires injection of counterfeit devices in the supply chain, which is a very powerful adversary model.
Another way to obtain access to secret entropy with βunlimited supply chain timeβ is to observe side channels while the SoC is executing. Because Caliptra is expected to be a <1 mm2 fraction of a large SoC, side-channel mitigation is required only against extremely resourceful attackers that can wade through and discern a large number of confounding signals and power profiles. With that priority in mind, DPA and DMA attacks should be mitigated via decoy value generation.
Any private key material or symmetric key material embedded in the RTL (and therefore βglobalβ) must be treated as having low value, reaching zero value in a number of quarters. A supply chain attacker can destructively obtain the key material, and loss of one part is not going to trigger any alarms.
Mitigation against SCA is not trivial and may be implemented in a variety of ways. Reference 7 provides a comprehensive overview of methods and techniques used in various SCA as well as recommendations for countermeasures against such attacks (including feasibility and applicability). Additionally, there are academic papers available from NIST and other resources that discuss SCA and their countermeasures.
Compliance and certification requirements
Due to the identity service surface offered to other SoC subsystems, Caliptra may fall under the Target of Evaluation (ToE) of an application that wishes to attain a specific compliance level for business reasons.
It is important to highlight that itβs not necessary for the RTM itself to unilaterally attain (for example, FIPS 140-3 L3). It is only relevant when the RTM is included in the βbagβ that wants to obtain a compliance certification. For example, it is relevant when a cloud provider wants to FIPS-certify PCIe link encryption in transit rooted to an ASIC identity emanating from a Caliptra instance.
See Reference 8 for requirements related to keys, entropy, random bits, cryptographic modules, and algorithms.
Known Answer Test (KAT) support
To certify a cryptographic module, pre-operational self-tests must be performed when the system is booted. Implementing KATs is required for FIPS certification. However, regardless of FIPS certification, it is considered a security best practice to ensure that the supported cryptographic algorithms are functioning properly to guarantee correct security posture.
KAT execution is described as two types:
- Pre-operational Self-Test (POST)
- Conditional Algorithm Self-Test (CAST)
A detailed description of the POST and CAST KATs can be found at csrc.nist.gov.
Table 14: KAT failure mitigations
| KAT type | If fails |
|---|---|
| POST | Failure of a POST KAT (for example, ECDSA) shall result in Caliptra boot failure. A reset may or may not result in successful POST completion. |
| CAST | Failure of a CAST KAT shall cause Caliptra to fail any operation that has a dependency on the associated cryptographic algorithm. |
Table 15: POST/CAST usage
| Crypto algorithm | Caliptra Boot ROM | Caliptra FMC | Caliptra Runtime FW |
|---|---|---|---|
| ECDSA8 | Yes | Yes | Yes |
| MLDSA-878 | Yes | Yes | Yes |
| AES | Yes | No | No |
| SHA9 | Yes | Yes | Yes |
| DRBG | No | No | No |
| HMAC | Yes (CDI generation) | No | No |
| KDF | Yes | Yes | No |
As shown in Table 15: POST/CAST usage, since the cryptographic algorithms required by the Caliptra Boot ROM are considered POSTs, and those same algorithms are used by Caliptra FMC and FW, there is no requirement that FMC and Runtime FW implement CASTs for those algorithms.
Firmware image format and verification
Caliptra supports verifying firmware with ECDSA P384 and MLDSA-87 signatures and Leighton-Micali Hash-based Signatures (LMS) in accordance with the requirements described in Reference 3.
Caliptra firmware is composed of two images: an FMC image and an application firmware image. A single firmware manifest describes these images. The manifest consists of a preamble (Table 16), a header (Table 17), and a Table of Contents (TOC) (Table 18). The image layout is shown in Figure 7: firmware image layout.
Figure 7: Firmware image layout
To verify the firmware, Caliptra ROM performs the following steps:
- Calculates the hash of the vendor key descriptors in the preamble and compares it against the hash in fuses (key_manifest_pk_hash). If the lifecycle is not "unprovisioned" and the hashes do not match, the boot fails.
- Calculates the hashes of the active vendor public keys and compares it against the hashes in the key descriptors identified by the corresponding active key indices.
- Calculates the hash of the owner public keys in the preamble and compares it against the hash in fuses (owner_pk_hash). If the owner_pk_hash fuse is not zero (i.e., unprovisioned) and hashes do not match, the boot fails. See the Owner authorization section.
- Ensures the vendor public key(s) selected by the preamble indices are not revoked based on fuse: key_manifest_pk_hash_mask for ECDSA public key, pqc_revocation for LMS or MLDSA public key.
- Calculates the hash of the first byte through the vendor data field of the header (this includes the TOC digest). This is the vendor firmware digest.
- Calculates the hash of the first byte through the owner data field of the header (this includes the TOC digest). This is the owner firmware digest.
- Verifies the vendor signature using the vendor firmware digest from step 5 and the vendor key(s) from step 4.
- Verifies the owner signature using the owner firmware digest from step 6 and the owner key(s) from step 3.
- Verifies the TOC against the TOC digest that was verified in steps 6 and 7. The TOC contains the SVN and digests for the FMC and runtime images.
- Verifies the FMC against the FMC digest in the TOC.
- Verifies the runtime against the runtime digest in the TOC.
- If Caliptra is not in "unprovisioned" lifecycle state or "anti-rollback disable" state, ROM compares the firmware's SVN against the SVN fuse (fuse_firmware_svn).
In addition to cold boot, Caliptra ROM performs firmware verification on hitless updates. See the hitless update section for details.
Table 16: Firmware manifest preamble
Fields are little endian unless described otherwise.
| Field | Size (bytes) | Description |
|---|---|---|
| Firmware Manifest Marker | 4 | Magic Number marking the start of the package manifest. The value must be 0x434D414E (βCMANβ in ASCII) |
| Firmware Manifest Size | 4 | Size of the full manifest structure |
| Firmware Manifest Type | 4 | Byte0: - Type 0x1 β ECDSA & LMS Keys 0x2 β ECDSA & MLDSA Keys Byte1-Byte3: Reserved |
| Vendor ECDSA Key Descriptor | 196 | Public Key Descriptor for ECDSA keys |
| Vendor LMS or MLDSA Key Descriptor | 1540 | Public Key Descriptor for LMS (1540 bytes) or MLDSA (196 bytes + 1344 unused bytes) keys |
| Active ECDSA Key Index | 4 | Public Key Index for the active ECDSA key |
| Active ECDSA Key | 96 | ECDSA P384 public key used to verify the Firmware Manifest Header Signature X-Coordinate: Public Key X-Coordinate (48 bytes, big endian) Y-Coordinate: Public Key Y-Coordinate (48 bytes, big endian) |
| Active LMS or MLDSA Key Index | 4 | Public Key Index for the active LMS or MLDSA key |
| Active LMS or MLDSA Key | 2592 | LMS public key (48 bytes + 2544 unused bytes) used to verify the Firmware Manifest Header Signature. tree_type: LMS Algorithm Type (4 bytes, big endian) Must equal 12. otstype: LM-OTS Algorithm Type (4 bytes, big endian) Must equal 7. id: (16 bytes) digest: (24 bytes) OR MLDSA-87 public key used to verify the Firmware Manifest Header Signature. (2592 bytes) |
| Vendor ECDSA Signature | 96 | Vendor ECDSA P384 signature of the Firmware Manifest header hashed using SHA384. R-Coordinate: Random Point (48 bytes, big endian) S-Coordinate: Proof (48 bytes, big endian) |
| Vendor LMS or MLDSA Signature | 4628 | Vendor LMS signature (1620 bytes + 3008 unused bytes) of the Firmware Manifest header hashed using SHA384. q: Leaf of the Merkle tree where the OTS public key appears (4 bytes) ots: LM-OTS Signature (1252 bytes) tree_type: LMS Algorithm Type (4 bytes, big endian) Must equal 12. tree_path: Path through the tree from the leaf associated with the LM-OTS signature to the root. (360 bytes) OR Vendor MLDSA-87 signature of the Firmware Manifest header hashed using SHA512 (4627 bytes + 1 Reserved byte). |
| Owner ECDSA Key Descriptor | 52 | Public Key Descriptor for ECDSA key |
| Owner LMS or MLDSA Key Descriptor | 52 | Public Key Descriptor for LMS or MLDSA key |
| Owner ECDSA Public Key | 96 | ECDSA P384 public key used to verify the Firmware Manifest Header Signature. X-Coordinate: Public Key X-Coordinate (48 bytes, big endian) Y-Coordinate: Public Key Y-Coordinate (48 bytes, big endian) |
| Owner LMS or MLDSA Public Key | 2592 | LMS public key (48 bytes + 2544 unused bytes) used to verify the Firmware Manifest Header Signature. tree_type: LMS Algorithm Type (4 bytes, big endian) Must equal 12. otstype: LM-OTS Algorithm Type (4 bytes, big endian) Must equal 7. id: (16 bytes) digest: (24 bytes) OR MLDSA-87 public key used to verify the Firmware Manifest Header Signature. (2592 bytes) |
| Owner ECDSA Signature | 96 | Vendor ECDSA P384 signature of the Firmware Manifest header hashed using SHA384. R-Coordinate: Random Point (48 bytes, big endian) S-Coordinate: Proof (48 bytes, big endian) |
| Owner LMS or MLDSA Signature | 4628 | Owner LMS signature (1620 bytes + 3008 unused bytes) of the Firmware Manifest header hashed using SHA384. q: Leaf of the Merkle tree where the OTS public key appears (4 bytes) ots: LM-OTS Signature (1252 bytes) tree_type: LMS Algorithm Type (4 bytes, big endian) Must equal 12. tree_path: Path through the tree from the leaf associated with the LM-OTS signature to the root. (360 bytes) OR Owner MLDSA-87 signature of the Firmware Manifest header hashed using SHA512 (4627 bytes + 1 Reserved byte). |
| Reserved | 8 | Reserved 8 bytes |
Table 17: Public Key Descriptor
Fields are little endian unless described otherwise.
| Field | Size (bytes) | Description |
|---|---|---|
| Key Descriptor Version | 1 | Version of the Key Descriptor. The value must be 0x1 for Caliptra 2.x |
| Intent | 1 | Type of the descriptor 0x1 - Vendor 0x2 - Owner |
| Key Type | 1 | Type of the key in the descriptor 0x1 - ECC 0x2 - LMS 0x3 - MLDSA |
| Key Hash Count | 1 | Number of valid public key hashes |
| Public Key Hash(es) | 48 * n | List of valid and invalid (if any) SHA2-384 public key hashes. ECDSA: n = 4, LMS: n = 32, MLDSA: n = 4 |
Table 18: Firmware manifest header
Fields are little endian unless described otherwise.
| Field | Size (bytes) | Description |
|---|---|---|
| Revision | 8 | 8-byte version of the firmware image bundle |
| Vendor ECDSA public key hash index | 4 | The hint to ROM to indicate which ECDSA public key hash it should use to validate the active ECDSA public key. |
| Vendor LMS or MLDSA public key hash index | 4 | The hint to ROM to indicate which LMS or MLDSA public key hash it should use to validate the active public key. |
| Flags | 4 | Feature flags. Bit0: - Interpret the pl0_axi_user field. If not set, all AXI_USERs are PL1 Bit1-Bit31: Reserved |
| TOC Entry Count | 4 | Number of entries in TOC. |
| PL0 AXI_USER | 4 | The AXI_USER with PL0 privileges. This value is used by the RT FW to verify the caller privilege against its AXI_USER. The AXI_USER is wired through AXI. |
| TOC Digest | 48 | SHA384 Digest of table of contents. |
| SVN | 4 | Security Version Number for the firmware, checked against the SVN fuses. |
| Vendor Data | 40 | Vendor Data. Not Before: Vendor Start Date [ASN1 Time Format] for Caliptra-issued certificates (15 bytes) Not After: Vendor End Date [ASN1 Time Format] for Caliptra-issued certificates (15 bytes) Reserved: (10 bytes) |
| Owner Data | 40 | Owner Data. Not Before: Owner Start Date [ASN1 Time Format] for Caliptra-issued certificate. Takes precedence over vendor start date (15 bytes) Not After: Owner End Date [ASN1 Time Format] for Caliptra-issued certificates. Takes precedence over vendor end date (15 bytes) Reserved: (10 bytes) |
Table 19: Table of contents
Fields are little endian unless described otherwise.
| Field | Size (bytes) | Description |
|---|---|---|
| TOC Entry Id | 4 | TOC Entry ID. The fields can have the following values: 0x0000_0001: FMC 0x0000_0002: Runtime |
| Image Type | 4 | Image Type that defines the format of the image section 0x0000_0001: Executable |
| Image Revision | 20 | Git Commit hash of the build |
| Image Version | 4 | Firmware release number |
| Image Load Address | 4 | Load address |
| Image Entry Point | 4 | Entry point to start the execution from |
| Image Offset | 4 | Offset from beginning of the image |
| Image Size | 4 | Image Size |
| Image Hash | 48 | SHA384 hash of image |
Post-Quantum Cryptography (PQC) requirements
Recent guidance from the US Government, CNSA 2.0, requests the use of LMS by 2025. Caliptra has an option to require LMS signatures in addition to ECDSA signatures (vendor and owner).
Based on the recommendation in CNSA 2.0, Caliptra uses the SHA256/192 algorithm. To provide a balance between the number of signatures allowed and signature size, Caliptra uses an LMS tree height of 15. This is referred to in NIST SP 800-208 as the LMOTS_SHA256_N24_W4 and LMS_SHA256_M24_H15 parameter sets.
Caliptra supports 32 LMS trees for the vendor and 1 tree for the owner. The SoC can support multiple trees for the owner via ownership transfer. It is recommended that the LMS trees are created from multiple HSMs that are geographically distributed.
Caliptra has an option starting in 2.0 to use ML-DSA-87 signatures in addition to ECDSA to support FIPS 204 and CNSA 2.0 requirements for category 5.
Caliptra provides cryptographic services to support ML-KEM (in addition to ECDH) key exchanges.
Key rotation
Firmware signing key rotation shall follow the requirements described in Reference 3.
Hardware
Please refer to Caliptra HW specification -> https://github.com/chipsalliance/caliptra-rtl/blob/main/docs/CaliptraHardwareSpecification.md
Passive Caliptra FW Load flow
Figure 10: Passive Caliptra FW load flow

- After the Caliptra microcontroller is out of reset, ROM starts executing and triggers the crypto block to run the UDS decrypt flow.
- Caliptra ROM enables the mailbox. (Until this point, any accidental or non-accidental writes that target the mailbox are dropped by the hardware.)
- Caliptra ROM asserts READY_FOR_FW wire. This is done by writing an internal register. This register is also visible to read on the AXI interface. SoC can choose to poll on this bit instead of using the wire (it is the SoC integration choice).
- SoC follows the mailbox protocol and pushes Caliptra FW into the mailbox.
- Caliptraβs mailbox HW asserts an interrupt to the microcontroller after the GO is written, per mailbox protocol. See Mailbox for specifics.
- After Caliptraβs FW is authenticated and loaded into ICCM, microcontroller runs the firmware and asserts READY_FOR_RTFLOWS wire.
Subsystem FW Load flow for Subsystem Mode
Please see the subsystem architecture section below.
CPU warm reset or PCIe hot reset flow β Caliptra IP reset
Figure 11: Hardware reset flow

Note: Since Caliptra IP may be placed in an ACPI S5 domain of the device, there may be devices where Caliptra IP may not go through reset on a device hot reset or CPU warm reset. But the flow shows what happens when such a reset happens.
- Caliptra IPβs reset is asserted by the SoC.
- Caliptraβs internal BootFSM resets the microcontroller and then resets all of the logic (including the SoC-facing AXI interface). Only registers or flops that are sitting on powergood are left with the same value. Note that SRAMs do not have a reset.
- Caliptra IPβs reset is deasserted by the SoC.
- At this point, the HW boot flow is the same as the cold boot flow. SoC is required to configure the internal TRNG and ROM WDT and then set CPTRA_FUSE_WR_DONE. This causes Caliptra IP to deassert the Ready_for_Fuse wire.
- Caliptraβs ROM reads an internal register to differentiate between warm, cold, and impactless flow. If it's a warm reset flow, then it skips DICE key generation and FW load flows (because keys were already derived and FW is already present in ICCM). This is an important reset time optimization for devices that need to meet the hot reset specification time.
Because warm reset is a pin input to Caliptra, Caliptra may not be idle when a warm reset occurs. If a warm reset occurs while Caliptra ROM, FMC, or RT initialization code is executing, Caliptra may be inoperable until a subsequent cold reset. If a warm reset occurs while Caliptra runtime is servicing a request, Caliptra shall remain operable but may refuse to wield production assets for subsequent requests.
Note: The cold reset flow is not explicitly mentioned but it is the same as the cold boot flow because Caliptra IP has no state through a cold reset. Note: Subsystem mode's warm reset flow is the same as above, except the warm reset action is triggered/managed by MCU.
Random Number Generator
TRNG is the digital logic and algorithms that are required for random number generation. It requires a physical entropy source input. See the Caliptra hardware specification and integration specification here for more information on the implementation and connectivity of TRNG.
- For SoCs that want to use their own legacy TRNG, Caliptra provides a HW API to push the random number on the request/response handshake. Details on this are also available in the Caliptra hardware specification and integration specification. This mode is advised for early development but discouraged for production tape outs due to the lower security assurances of an external TRNG.
- In Subsystem mode, Caliptra integrators are required to use the internal TRNG.
Mailbox
The Caliptra Mailbox is a 128 KiB buffer that is used to exchange data between the SoC and the Caliptra microcontroller.
The SoC communicates with the mailbox over an AXI interface. This allows the SoC to identify the device that is using the interface. This ensures that the mailbox, control registers, and fuses are read or written only by the appropriate device.
When a mailbox is populated by SoC, an interrupt to the FW occurs. This indicates that a command is available in the mailbox. The microcontroller is responsible for reading from and responding to the command.
When a mailbox is populated by the microcontroller, Caliptra sends a wire indication to the SoC that a command is available in the mailbox as well as updating the MAILBOX STATUS register. The SoC is responsible for reading from and responding to the command.
Mailboxes are generic data passing structures, and the Caliptra hardware only enforces the protocol for writing to and reading from the mailbox. How the command and data are interpreted by the FW and SoC are not enforced in Caliptra.
Sender protocol
Sending data to the mailbox:
- Requester queries the mailbox by reading the LOCK control register.
- If LOCK returns 0, LOCK is granted and is set to 1.
- If LOCK returns 1, MBOX is locked for another device.
- Requester writes the command to the COMMAND register.
- Requester writes the data length in bytes to the DLEN register.
- Requester writes data packets to the MBOX DATAIN register.
- Requester writes to the EXECUTE register.
- Requester reads the STATUS register until a value other than CMD_BUSY is detected. This is also indicated by a dedicated wire that asserts when STATUS is changed. STATUS can return:
- DATA_READY β Indicates the return data is in the mailbox for the requested command.
- CMD_COMPLETE β Indicates the successful completion of the requested command.
- CMD_FAILURE β Indicates the requested command failed.
- CMD_BUSY β Indicates the requested command is still in progress.
- If response data is expected and the requester reads a STATUS of DATA_READY:
- Requester reads from the DLEN register to ascertain the size of the response data.
- Requester reads data from the DATAOUT register until DLEN bytes are read.
- After reading the response data, if any, requester writes 0 to the EXECUTE register to clear LOCK.
Notes on behavior: After LOCK is granted, the mailbox is locked until that device has concluded its operation. The mailbox is responsible only for accepting writes from the device that requested and locked the mailbox.
Figure 12: Mailbox sender flow

Receiver protocol
Upon receiving an indication that the mailbox is populated, the appropriate device can read the mailbox. This is indicated by a dedicated wire that is asserted when Caliptra populates the mailbox for SoC consumption.
Receiving data from the mailbox:
- Receiver reads the COMMAND register.
- Receiver reads the DLEN register.
- Receiver reads the MBOX DATAOUT register.
- Continues reading MBOX DATAOUT register until DLEN bytes are read.
- If response data is required:
- Receiver writes the size of the response data to the DLEN register.
- Receiver writes to the DATAIN register until DLEN bytes are written.
- Note: Response data is only supported for Caliptra, to SoC-initiated commands. SoC cannot provide response data for Caliptra-initiated commands.
- Receiver writes to the STATUS register.
- If response data was provided, STATUS should be written to DATA_READY.
- Otherwise, STATUS should be written to CMD_COMPLETE (or CMD_FAILURE).
Figure 13: Mailbox receiver flow

User attributes
The AXI_USER field of the AXI interface is used to encode device attributes for the requester that is utilizing the SoC interface. These values can be used for:
- Ensuring the device that was granted the LOCK is the one that accesses the MBOX, DLEN, COMMAND, and STATUS registers.
- Prioritizing who is next granted the LOCK.
- Mailbox user 0xFFFF_FFFF is reserved for Caliptra internal use. Caliptra firmware will fail any SoC requests from this user.
Mailbox commands
The Caliptra mailbox commands are specified in the Caliptra runtime firmware specification.
Cryptographic mailbox commands
Cryptographic mailbox (CM) commands are a flexible set of mailbox commands that provide access to Caliptra's cryptographic cabilities. This is meant for key storage and use to support protocols like SPDM and OCP LOCK.
These commands are not meant to be high-performance as they are accessed via mailbox commands.
Key material and data will be stored in an encrypted and authenticated section of DCCM. Keys are used via handles that refer to portions of DCCM.
These mailbox commands extend Caliptra's cryptographic support to include SHA, HMAC, HKDF, AES, ECDH, ML-KEM, and RNG services in addition ECDSA and ML-DSA.
The runtime firmware specification contains further details.
Hash calculation HW API (Subsystem mode only)
Caliptra provides a HW API to do a SHA384 hash calculation. The SoC can access the accelerator through the Caliptra FW API only in subsystem mode. Caliptra FW API uses the internal SHA accelerator and its DMA widget be hash the required data and present it back to Calitpra FW.
JTAG/TAP debug
Figure 14: Debug flow

- SoC drives the security state indicating that it is a debug flow. See Caliptra security states for encodings.
- SoC (using a GPIO pin or SoC ROM) drives BootFSMBrk (this is also used for debug cases). This can be driven at any time before cptra_rst_b is deasserted.
- SoC follows the boot flow as defined in Caliptra IP HW boot flow to assert cptra_pwrgood and deassert cptra_rst_b, followed by writing to the fuse registers.
- HVM, through JTAG or through the Caliptra SoC interface, writes to βCPTRA_DBG_MANUF_SERVICE_REGβ, requesting the appropriate debug service (see the ROM/Runtime specifications for bit definitions).
- HVM, through JTAG, can also inject microcontroller TAP commands at this point following the VeeR Specification.
- HVM, through JTAG or through the Caliptra SoC interface, writes to βCPTRA_BOOTFSM_GOβ to allow Caliptraβs internal BootFSM to continue to bring up microcontroller out of reset.
- Microcontroller executes the appropriate debug service.
- Specific SoC interface registers are accessible over the JTAG interface in debug (or manufacturing mode). The following are the JTAG register addresses for these registers.
Table 20: JTAG accessible registers
| Register name | JTAG address | Accessibility |
|---|---|---|
| MBOX_LOCK | 7'h75 | RO |
| MBOX_CMD | 7'h76 | RW |
| MBOX_DLEN | 7'h50 | RW |
| MBOX_DOUT | 7'h51 | RO |
| MBOX_DIN | 7'h62 | WO |
| MBOX_STATUS | 7'h52 | RW |
| MBOX_EXECUTE | 7'h77 | WO |
| BOOT_STATUS | 7'h53 | RO |
| CPTRA_HW_ERRROR_ENC | 7'h54 | RO |
| CPTRA_FW_ERROR_ENC | 7'h55 | RO |
| SS_UDS_SEED_BASE_ADDR_L | 7'h56 | RO |
| SS_UDS_SEED_BASE_ADDR_H | 7'h57 | RO |
| HW_FATAL_ERROR | 7'h58 | RO |
| FW_FATAL_ERROR | 7'h59 | RO |
| HW_NON_FATAL_ERROR | 7'h5a | RO |
| FW_NON_FATAL_ERROR | 7'h5b | RO |
| CPTRA_DBG_MANUF_SERVICE_REG | 7'h60 | RW |
| BOOTFSM_GO | 7'h61 | RW |
| SS_DEBUG_INTENT | 7'h63 | RW |
| SS_CALIPTRA_BASE_ADDR_L | 7'h64 | RW |
| SS_CALIPTRA_BASE_ADDR_H | 7'h65 | RW |
| SS_MCI_BASE_ADDR_L | 7'h66 | RW |
| SS_MCI_BASE_ADDR_H | 7'h67 | RW |
| SS_RECOVERY_IFC_BASE_ADDR_L | 7'h68 | RW |
| SS_RECOVERY_IFC_BASE_ADDR_H | 7'h69 | RW |
| SS_OTP_FC_BASE_ADDR_L | 7'h6A | RW |
| SS_OTP_FC_BASE_ADDR_H | 7'h6B | RW |
| SS_STRAP_GENERIC_0 | 7'h6C | RW |
| SS_STRAP_GENERIC_1 | 7'h6D | RW |
| SS_STRAP_GENERIC_2 | 7'h6E | RW |
| SS_STRAP_GENERIC_3 | 7'h6F | RW |
| SS_DBG_MANUF_SERVICE_REG_REQ | 7'h70 | RW |
| SS_DBG_MANUF_SERVICE_REG_RSP | 7'h71 | RW |
| SS_DBG_UNLOCK_LEVEL0 | 7'h72 | RW |
| SS_DBG_UNLOCK_LEVEL1 | 7'h73 | RW |
| SS_STRAP_CALIPTRA_DMA_AXI_USER | 7'h74 | RW |
Note: These are injected as TAP register read/write commands as defined in the VeeR Specification.
Notes:
- The security state is latched at cptra_rst_b deassertion. If the security state is unlocked at this time, only TAP is opened.
- Changing the security state at a random time after cptra_rst_b deassertion (Caliptra in passive mode): does not unlock Caliptra JTAG.
- Changing the security state at a random time after cptra_rst_b deassertion (Caliptra in passive mode): transition from debug locked to debug unlocked has no effect until next reset.
- Changing the security state at a random time after cptra_rst_b deassertion (Caliptra in subsystem mode): Refer to Production Debug Unlock Architecture for details of the full procedure.
- Setting scan mode at a random time after cptra_pwrgood: flushes out all internal Caliptra secrets, assets, key vault, and crypto intermediate state.
- Scan mode assertion does not automatically open Caliptra JTAG.
Architectural registers
These registers are accessible over AXI to be read according to the register access permissions. For more information, see the register reference manual at https://ereg.caliptra.org.
Fuse requirements
Fuse registers are programmable whenever IP goes through reset (after cptra_rst_b asserts and de-asserts) and before the fuse registers are locked from writes. If the lock was set, the writes are dropped. The lock is sticky across a warm reset.
To ensure that the security claims of Caliptra are achieved, specific fuse protection capabilities must be supported:
- Fuses that hold Caliptra secrets shall not be readable by any mutable code in the SoC. The intent is to provide defense-in-depth for the UDS.
- For SoCs that intend to achieve FIPS 140-3 CMVP certification with Caliptra, SoC shall implement a HW state machine for copying the uds_seed, field_entropy, and key_manifest_pk_hash fuses from the fuse controller into Caliptra's fuse shadow registers. Only the fuse controller, this state machine, and Caliptra shall be able to access these fuse values. The intent is to not include the SoC ROM as part of the cryptographic module boundary.
- For the production lifecycle state, if JTAG is enabled pre or post SoC reset, then Caliptra's dedicated fuses shall not be accessible (shall not be readable, shall not be writable) by Caliptra or other SoC IP. This restriction shall be applicable to Caliptra's fuse shadow registers as well (see Physical Attack Countermeasures).
- SoC should ensure that the integrity of each fuse is maintained through the life of the part. The integrity of the fuses can be maintained by fuse redundancy, ECC, or other means determined sufficient by the SoC.
Fuse programming
All fuse based cryptographic keying material and seeds (for example, UDS Seed) shall be generated (on-chip or off-chip) per requirements described in Reference 8.
SoC shall support in-field programmable fusing. Fuse Map shows which fuses are expected to be in-field programmable. SoCs shall implement authorization for in-field programmable fusing to mitigate denial-of-service attacks. Authorization design is outside the scope of this specification. In Subsystem mode, SoC may use MCU RT FW for these actions.
SoC shall support a field entropy programming API. The API shall support retrieving an input value from an external interface. It should cryptographically mix that value with the output of an on-die TRNG to generate the field entropy value. The API implementation shall burn the field entropy value into the first available field entropy fuse slot (or fail if no slots are available). Caliptra is expected to be in any security state. The device owner is expected to call this API in a βclean room environmentβ to minimize risk of attack on the programming process. In Subsystem mode, SoC may use MCU RT FW for these actions.
Fuse zeroing
Caliptra assumes that the unfused value in fuses is '0' and the fused value is '1'. With this context, zeroization refers to destroying a secret value by fusing it to all ones.
For SoCs that intend to achieve FIPS 140-3 CMVP certification with Caliptra:
- SoC shall implement zeroization for uds_seed and field_entropy such that the fuse rows holding these secrets contain all ones.
- SoC shall expose and document an API for a tester to invoke zeroization.
- SoC shall indicate that zeroization has occurred by statically asserting GENERIC_INPUT_WIRE[0] to Caliptra.
- SoC shall set Caliptraβs security state to DebugUnlock by ORing it with the zeroization status signal.
- SoC shall expose Caliptra architectural registers as API for a tester to read.
- SoC shall ensure authorization for this API to guard against denial-of-service attacks. The authorization design is left to the vendor.
- Note: In Subsystem mode, SoC should use MCU RT FW with the corresponding subsystem HW components for these actions.
Fuse map
FIXME: Needs updates for Caliptra 2p0 & Subsystem The following table describes Caliptra's fuse map:
Table 21: Caliptra Fuse Map
| Name | Size (bits) | ACL | Fuse programming time | Description |
|---|---|---|---|---|
| UDS SEED (obfuscated) | 512 | ROM | SoC manufacturing | DICE Unique Device Secret Seed. This seed is unique per device. The seed is scrambled using an obfuscation function. |
| FIELD ENTROPY (obfuscated) | 256 | ROM | Device owner in-field programmable | Field-programmable by the owner, used to hedge against UDS disclosure in the supply chain. |
| VENDOR PK HASH | 384 | ROM FMC RUNTIME | SoC manufacturing | SHA384 hash of the Vendor ECDSA P384 and LMS or MLDSA Public Key Descriptors. |
| ECC REVOCATION | 4 | ROM FMC RUNTIME | In-field programmable | One-hot encoded list of revoked Vendor ECDSA P384 Public Keys (up to 4 keys). |
| OWNER PK HASH | 384 | ROM FMC RUNTIME | In-field programmable | SHA384 hash of the Owner ECDSA P384 and LMS or MLDSA Public Keys. |
| FMC KEY MANIFEST SVN | 32 | ROM FMC RUNTIME | In-field programmable | FMC security version number. (Deprecated in 2.0, will be removed in a future RTL revision.) |
| RUNTIME SVN | 128 | ROM FMC RUNTIME | In-field programmable | Firmware security version number. |
| ANTI-ROLLBACK DISABLE | 1 | ROM FMC RUNTIME | SoC manufacturing or in-field programmable | Disables anti-rollback support from Caliptra. (For example, if a Platform RoT is managing FW storage and anti-rollback protection external to the SoC.) |
| IDEVID CERT IDEVID ATTR | 768, 352 used | ROM FMC RUNTIME | SoC manufacturing | IDevID Certificate Generation Attributes. See IDevID certificate section. Caliptra only uses 352 bits. Integrator is not required to back the remaining 416 bits with physical fuses. |
| IDEVID MANUF HSM IDENTIFIER | 128, 0 used | ROM FMC RUNTIME | SoC manufacturing | Spare bits for Vendor IDevID provisioner CA identifiers. Caliptra does not use these bits. Integrator is not required to back these with physical fuses. |
| LIFE CYCLE | 2 | ROM FMC RUNTIME | SoC manufacturing | Caliptra Boot Media Integrated mode usage only. SoCs that build with a Boot Media Dependent profile donβt have to account for these fuses. - '00 - Unprovisioned - '01 - Manufacturing - '10 - Undefined - '11 - Production Reset: Can only be reset on powergood. |
| LMS REVOCATION | 32 | ROM | In-field programmable | One-hot encoded list of revoked Vendor LMS Public Keys (up to 32 keys). |
| MLDSA REVOCATION | 4 | ROM | In-field programmable | One-hot encoded list of revoked Vendor MLDSA Public Keys (up to 4 keys). |
| SOC STEPPING ID | 16 | ROM FMC RUNTIME | SoC manufacturing | Identifier assigned by vendor to differentiate silicon steppings. |
| MANUF_DEBUG_UNLOCK_TOKEN | 512 | ROM | SoC manufacturing | Digest value for manufacturing debug unlock token secret value for authorization. |
| PQC Key Type | 2 | ROM FMC RUNTIME | In-field programmable | One-hot encoded selection of PQC key type for firmware validation. - Bit 0 - MLDSA - Bit 1 - LMS |
| SOC MANIFEST SVN | 128 | ROM FMC RUNTIME | In-field programmable | One-hot encoded value for the SOC authorization manifest minimum supported SVN. |
| SOC MANIFEST MAX SVN | 8 | ROM FMC RUNTIME | In-field programmable | Maximum value for the SOC authorization manifest SVN. |
| HEK RATCHET SEED | 256 | ROM | In-field programmable | OCP L.O.C.K. seed used by hardware to generate HEK |
Error reporting and handling
This section describes Caliptra error reporting and handling.
Table 22: Hardware and firmware error types
| Fatal errors | Non-fatal errors | |
|---|---|---|
| Hardware | - ICCM, DCCM SRAM ECC. - The second watchdog (WD) timer expiry triggers an NMI, and a FATAL error is signaled to the SoC. - Operating HMAC, ECC, or DOE engines simultaneously. | - Mailbox SRAM ECC (except initial firmware load) - Mailbox incorrect protocol or commands. For example, incorrect access ordering or access without Lock. |
| Firmware | - Control Flow Integrity (CFI) errors. - KAT errors. - FIPS Self Test errors. - Mailbox commands received after FIPS Shutdown request completes. - Hand-off errors detected upon transfer of control from ROM to FMC or FMC to Runtime. - Mailbox protocol violations leading the mailbox to an inconsistent state if encountered by ROM during cold reset flow. - Firmware image verification or authentication failures if encountered by ROM during Cold Reset flow. - Non-aligned access to ICCM or DCCM - AHB access hangs, triggered through WD timer expiry - AHB access outside of the decoding range | - Firmware image verification or authentication failures if encountered by ROM during Update Reset flow. - Mailbox protocol violations leading the mailbox to an inconsistent state (if encountered by ROM during Update Reset flow). - Cryptography processing errors. |
Fatal errors
- Fatal errors log the FATAL ERROR reasons into an architectural register that is RW from the external world. This register must be sticky (as in, reset is on powergood).
- This register may be cleared at any time via register write (W1C).
- Caliptra signals fatal errors using a cptra_error_fatal wire.
- SoCs should connect this into their SoC error handling logic. Upon detection of a FATAL ERROR in Caliptra, SoC shall treat any outstanding commands with Caliptra as failed, and SoC may recover by performing a Caliptra reset using the signal
cptra_rst_b. - This signal prevents forward progress of the boot process if measurement submission to Caliptra fails. If SoC detects a Caliptra fatal error while the SoC is in steady state, then there is no obligation for the SoC to immediately address that error. If rebooting the SoC for such failures is deemed unacceptable to uptime, the SoC should implement the ability to trigger a Caliptra warm reset independently of the SoC, and may use this mechanism to recover.
- Error mask registers (writable only by Caliptra microcontroller) may be used to prevent error signal assertion per-event. Mask registers only impact interrupts when they are set prior to the error occurrence.
- cptra_error_fatal remains asserted until Caliptra is reset. Note that, although the HW FATAL ERROR register fields may be cleared at any time, a reset is still required to clear the interrupt.
- SoCs should connect this into their SoC error handling logic. Upon detection of a FATAL ERROR in Caliptra, SoC shall treat any outstanding commands with Caliptra as failed, and SoC may recover by performing a Caliptra reset using the signal
- When a fatal error occurs, all volatile assets that are stored in key vault are cleared.
Non-fatal errors
- Non-fatal errors log the NON-FATAL ERROR reasons into an architectural register that is RW from the external world. This register must be sticky (as in, reset is on powergood).
- This register may be cleared at any time via register write (W1C).
- Caliptra signals non-fatal errors using a cptra_error_non_fatal wire.
- Caliptra reset via
cptra_rst_b, or a write to clear the NON-FATAL ERROR register, cause the interrupt to deassert. - It is optional for SoCs to include this signal in their logic.
Firmware errors
Please refer to the Caliptra code base for a list of the error codes.
Caliptra Security Subsystem
The Caliptra subsystem offers a complete RoT subsystem, with open source programmable components for customization of SoC boot flows.
Figure: Caliptra security subsystem

Caliptra Subsystem Trademark Compliance
- Caliptra subsystem trademark compliance shall have Caliptra Core 2.0, Life Cycle Controller, Fuse Controller, I3C with OCP streaming boot interface, Manufacture Control Unit (MCU) and Manufacturer Control Interface (MCI) taken as is without change to ensure there is hardware transparency and consistency.
- Caliptra subsystem provides life cycle state to the SoC.
SoC Integration Flexibility
- SoC may choose to add PLLs (Phase Locked Loop for stable clock generation), SoC specific mailboxes, SoC specific firewalls & address translations, environmental circuitry as some examples.
- Caliptra subsystem provides flexibility to SoC to remap subsystem driven debug levels to SoC specific debug policies.
- Please see Subsystem hardware and integration specifications for additional details on subsystem configurability (FIXME: Add md versions once available; right now they are uploaded as pdfs in Caliptra-SS repository's doc folder).
Caliptra Subsystem Architectural Flows
Subsystem (Pre-FW Load) Boot Flow
Note: Any step done by MCU HW/ROM would have been performed by βSoC Managerβ in Caliptra 1p0.
- SoC (using its HW or MCU ROM) performs pre-steps like bringing up CRO or PLL, MBIST flows on SRAMs, SRAM Init etc.
- SoC will assert MCU & Caliptra pwrgood and after 10 cycles MCU
- MCU ROM or SoC Manager wrapper will bring up fuse controller and any other SoC specific infrastructure RTL modules (I3C, GPIO programming, Glitch detector programming etc.)
- MCU ROM or SoC Manager wrapper will deassert Caliptra reset.
- Caliptra HW will read the security centric (secret) fuses.
- MCU ROM waits for ready_for_fuses to be asserted.
- MCU ROM or SoC Manager will populate the remaining fuses of Caliptra and reads its own fuses (if any). Note that this step is gated behind the completion of security fuse writes to ensure the step has completed.
- MCU ROM will write βfuse doneβ to Caliptra.
- Caliptra will go through its boot flow of bringing up uC.
- Caliptra ROM starts and executes various KATs flows.
Subsystem Boot Flow
If (Caliptra-Passive-Mode)
- SoC Manager goes through Caliptra 1.x flows => loads Caliptra FW using Caliptra 1.x flows, Caliptra sets RT ready and SOC <-> Caliptra boot flow is done.
(Caliptra-Subsystem-Mode)
- Caliptra ROM waits for SoC infrastructure readiness indication. If this indication is set, Caliptra will do the identity derviation flows. If it is not set, then this flow is run when the SoC infrastructure readiness indication is set.
- Caliptra ROM uses the streaming boot interface to load its firmware. Please see the specific section for next level specifics; At a high level, Caliptra ROM sets the device ready in the I3C controller and poll I3C for the payloads.
- BMC or a similar platform component will send the image (code or data) through OCP streaming boot interface. 1.Caliptra ROM uses the streaming boot interface to receive 'data' from the BMC or MCU. Examples include SoC specific configuration. Caliptra ROM operation of the streaming boot interface will be identical for receiving either 'code' or 'data'. The data flow and code flow over streaming boot interface is the same from physical interface point of view and follows the OCP recovery spec for streaming boot support as implemented in Caliptra subsystem documentation (please see the recovery section). 2. This need for data flow (from flash or BMC) is indicated by a SOC configuration bit to Caliptra ROM. 3. This βdataβ flow is possible only before SOC infra readiness is set. This is specifically used for scenarios where PUF or other characterization data is coming off-chip (say a flash or BMC). FIXME: Security and operations impact of this step/flow is being analyzed. This capability/flexibility will be updated or removed once that is finalized. 4. This data must be hashed into PCR0 5. To keep the scope limited, only one βdataβ flow is allowed
- If the data was required to be run (is indicated by a SOC configuration bit to Caliptra ROM), Caliptra ROM waits for SOC infrastructure readiness to be set. Once set, it will do the required key derivations.
- Caliptra ROM will read the streaming boot interface registers (data payload registers) over AXI manager interface and write into Caliptra MB SRAM. The offset of the streaming boot interface registers are available through a config register that is set at SOC integration time or by MCU ROM.
- Note that an intelligent I3C peripheral could βstreamβ the image. This is a future enhancement.
- Caliptra ROM will authenticate its image sitting in Caliptra MB SRAM
- Caliptra ROM flow will be similar to Caliptra 1.0 flow with PQC FW Authentication.
- Caliptra ROM will derive required keys similar to Caliptra 1.0 flow (while accounting for PQC)
- Caliptra ROM will switch to RT image.
- Caliptra RT FW will set the Streaming boot interface to allow BMCβs Recovery Agent (RA) to send the next image (which MUST be SOC image manifest).
- BMC RA is required to know the different component of the images using the similar manifestation as DSP0267 PLDM for Firmware Update over MCTP components.
- Caliptra RT FW will read the Streaming boot interface registers over AXI manager interface and write the image to its mailbox.
- Caliptra RT FW will authenticate SoC manifest using the keys available through Caliptra Image, authenticate the image, capture the measurement and capture the relevant information into DCCM.
- Caliptra RT FW will set the Streaming boot interface to allow BMCβs Recovery Agent (RA) to send the next image (which MUST be MCU image manifest).
- BMC RA is required to know the different component of the images using the similar manifestation as DSP0267 PLDM for Firmware Update over MCTP components.
- Caliptra RT FW will read the Streaming boot interface registers over AXI manager interface and write the image to MCU SRAM aperture (that is open to Caliptra only by HW construction).
- The address of the MCU SRAM is provided to Caliptraβs RT FW through SOC manifest.
- Note: From validation front, need to ensure the Caliptra ROM and MCU are aligned in endianness.
- Caliptra RT FW will instruct Caliptra HW to read MCU SRAM and generate the hash (Caliptra HW will use the SHA accelerator and AXI mastering capabilities to do this)
Open: Should we have a capability to do something like this for decryption too? (Key to be provided by MCU/SOC before running the decryption flow?)
- Caliptra RT FW will use this hash and verify it against the hash in the SOC manifest.
- Caliptra RT FW after verifying/authorizing the image and if it passes, it will set EXEC/GO bit into the register as specified in the previous command. This register write will also assert a Caliptra interface wire.
- MCU ROM will be polling the breadcrumb that the MCU SRAM has valid content and will jump to the MCU SRAM to execute from it. NOTE: This breadcrumb should be one of the status bits available on the MCU interface that is set by Caliptra GO bit wires.
- Until this step MCU SRAM aperture that holds the MCU RT FW and certain Streaming boot interface registers are not accessible to MCU.
- MCU RT FW will now set Streaming boot flow is completed.
- BMC or a similar platform component will now do MCTP enumeration flow to MCU over I3C.
- MCU RT FW is responsible for responding to all MCTP requests.
- MCU RT FW will do the PLDM T5 flow, extract FW or configuration payload, use Caliptra to authenticate and deploy the rest of the images as described in run-time authentication flows.
Figure: Subsystem Boot Flow

Common Run-time Authentication Flows
- MCU RT FW will do PLDM T5 flow to obtain the FW images for downstream uControllers (or other SoC configuration)
- MCU RT FW will send/stream the (FW or config) payload to Caliptra SHA Acc to perform hash measurements as the payload comes through the MCTP transport.
- MCU RT FW can either stage the entire image or write to the final destination as a part of the previous step depending on the SOC construction.
- Note: By SOC security/design construction, the FW/payload that is loaded must NOT be allowed to execute or be used until Caliptra authorizes that the FW/payload.
- MCU RT FW will issue the imageID & GO-field bit (bit that Caliptra RT FW would set if the image authorization is successful) to Caliptra RT FW to start off the process of image authorization of the image that was hashed.
- Caliptra RT FW will obtain this hash from the internal SHA accelerator register that was used to hash in the previous step.
- Caliptra RT FW after verifying/authorizing the image and if it passes, it will set EXEC/GO bit into the register as specified in the previous command. This register write will also assert a Caliptra interface wire.
- MCU RT FW has an option of looking at the Mailbox command success or read the register or use the wire that the register will drive to allow the execution of the FW. This wire allows SOCs to construct a hardened logic of allowing executions from ICCM/TCMs only after the wire is set.
- SOC construction outside of MCU SRAM is SOC specific construction and the spec here provides recommendations on using MCU and Caliptra to build such a logic. Please refer to Caliptra subsystem hardware specification for construction specifics (Hint: These functions are integrated into Manufacturer Control Interface [MCI]).
- Any logic outside of the Caliptra Subsystem boundary is SoC specific and will be custom to the SoC design. This specification provides recommendations for how Caliptra and MCU may be integrated into the SoC.
FIXME: Add the visio flow picture
Subsystem support for Hitless Updates
Caliptra Hitless Update
- Payloads of all hitless update come over DSP0267 PLDM for Firmware Update over MCTP flow to the MCU similar to the boot time flows.
- MCU provides SOC manifest to Caliptra and waits for authentication to be successful. If this wasnβt provided Caliptra will use the latest SOC manifest available.
- If failed, MCU uses DSP0267 PLDM for Firmware Update over MCTP to report the same to the update agent using PLDM protocol
- MCU provides the Caliptra FW using Caliptra Mailbox using the hitless update flows documented in the Caliptra specification
MCU Hitless Update
- Payloads of all hitless update come over DSP0267 PLDM for Firmware Update over MCTP flow to the MCU similar to the boot time flows.
- MCU provides SOC manifest to Caliptra and waits for authentication to be successful. If this wasnβt provided Caliptra will use the latest SOC manifest available.
- If failed, MCU uses DSP0267 PLDM for Firmware Update over MCTP to report the same to the update agent using PLDM protocol
- MCU stages the incoming FW payload in an SOC-defined staging SRAM and provides the MMIO address of the staging memory to Caliptra. It is better to keep this as a part of the authenticated SOC manifest (as a configuration) from a security perspective.
- Caliptra RT FW will use the Caliptra DMA engine to issue the read and hash the image (note that the length of the image must be part of the SOC manifest)
- Caliptra RT FW after verifying/authorizing the image and if it passes, waits for activate command to be issued from MCU. MCU will get this command over DSP0267 PLDM for Firmware Update over MCTP flow; At this point, Caliptra will reset EXEC/GO bit into the register as specified in the previous command. This register write will also deassert a Caliptra interface wire.
- MCU HW logic will use this indication to initiate MCU uC reset flow
- MCU HW logic sends a reset-go-req to MCU uC (an interrupt)
- MCU HW logic waits for reset-go-ack from MCU uC (Note that this handshake exists to ensure uController is in an appropriate quiescent state to take the reset)
- MCU HW logic will assert the reset to the MCU uC
- Caliptra RT FW will wait for reset assertion and then read the staged SRAM over AXI manager interface and write the image to the MCU SRAM aperture (that is open to Caliptra only by HW construction).
- The address of the MCU SRAM is provided to Caliptraβs RT FW through SOC manifest.
- Note: From the validation front, need to ensure the Caliptra ROM and MCU are aligned in endianness.
- Note: True downtime of MCU is from when its reset is asserted; It is SOC implementation requirement that it handles (eg. through buffering) all transactions to MCU while it is going through a hitless update.
- After the MCU SRAM is populated, Caliptra RT FW will set EXEC/GO bit into the register as specified in the previous command. This register write will also assert a Caliptra interface wire.
- MCU HW logic will use this indication to deassert the MCU reset.
- MCU ROM will look at the breadcrumb that the MCU SRAM has valid content and will start the execution from it directly. NOTE: This breadcrumb should be one of the status bits available on the MCU interface that is set by Caliptra GO bit wires.
SoC-FW Hitless Update
SoC may have other components that may need to be updated at run-time in a hitless/impactless manner.
The update flow will follow the same sequence as MCU Hitless update except they are executed by the MCU by using Caliptra as the RoT engine for doing all the required authentication/authorization flows.
Further SoCs may require the hitless update without impacting the workloads/VMs running on the host or the VMs using the devices. This essentially means that impactless update must happen without causing any timeouts to the in-flight transactions. While the treatment of those transactions are device dependent, Caliptra subsystem must provide a way to be able to authenticate and activate the FW in the shortest time possible.
Caliptra subsystem provides this architectural capability as follows:
- MCU provides SOC manifest to Caliptra and waits for authentication to be successful. If this wasnβt provided Caliptra will use the latest SOC manifest available.
- If failed, MCU uses DSP0267 PLDM for Firmware Update over MCTP to report the same to the update agent using PLDM protocol
- MCU stages all the incoming FW payload in an SOC-defined staging memory and provides the MMIO address of the staging SRAM to Caliptra. It is better to keep this as a part of the authenticated SOC manifest (as a configuration) from security perspective. (FW Arch requirement: This SOC-defined staging RAM MMIO offset which can be one for all the images or it could be per image, recommended to keep it the later way, should be defined in the SOC manifest.)
- Caliptra RT FW will use the Caliptra DMA engine to issue the read and hash the image (note that the length of the each image must be part of the SOC manifest)
- Caliptra RT FW will verify & authorize the images. It will also compare the hash of the images against the βcurrentβ hash of each of the image.
- MCU will send the βactivateβ command to Caliptra (which is part of PLDM spec that MCU understands)
- If MCU FW is updated/new, Caliptra will execute the MCU Hitless update flow.
- Caliptra RT FW will then set the GO bits for all the SoC FWs that are updated (vs what was already running)
- SoC specific logic & MCU RT FW will use this information to update the remaining FW using SoC specific architectural flows. Note: Since this work is mainly distribution of the FW to the destination uCs, SoC should be built to do this flow as fast as possible to meet workload non-interruption/impactless requirements.
Multi-Chiplet Flows
This section explain how generic FW Load Flows would function for SoCs with multiple chiplets that are required to have their security controller functions. It is plausible that a SoC is built with a single security controller active on one chiplet and that serves all other chiplets.
Note: Additional control signals that MCU would control are SoC specific and are implemented through SoC widget(s).
- Primary tile uses Caliptra-Subsystem-Mode at its silicon boot time
- Secondary tileβs MCU ROM will go through the same common boot flow as the primary tile (except the peripheral could be inter-chiplet link).
- Secondary tileβs MCU ROM will wait for inter-chiplet link to be available for use (this would be an indication to MCU ROM)
- Primary tileβs MCU RT FW will fetch the secondary tileβs FW using DSP0267 PLDM for Firmware Update over MCTP T5 flow and βstreamβ using the streaming boot interface protocol to the secondary tile(s).
- Based on SOC integration, inter-chiplet could be an intelligent peripheral that can DMA or implement data payload registers for Caliptra to read.
- Note that the indication from Caliptra for βnext-imageβ follows the same streaming boot interface protocol.
- Note that to load the remaining images of a secondary tile, SOC can choose to do streaming boot flow for rest of the remaining images. Depending on the SOC architecture and chiplets, MCU RT FW may coordinate the SOC to boot in such a way that it βbroadcastsβ the same image to multiple chiplets that require the same image. This is a SOC optimized flow outside of Caliptra or Subsystem Context.
Caliptra Subsystem I3C Streaming Boot Interface
The streaming boot interface is implemented as an I3C target with commands defined by the OCP Recovery specification. It will have a unique address compared to any other I3C endpoint for the device. It will comply with I3C Basic v1.1.1 specification. It will support I3C read and write transfer operations. It must support Max read and write data transfer of 1-260B excluding the command code (1 Byte), length (2 Byte), and PEC (1 Byte), total 4 Byte I3C header. Therefore, max streaming boot data per transfer will be limited to 256-byte data.
I3C streaming boot interface is responsible for the following list of actions:
- Responding to command sent by Recovery Agent (RA)
- Updating status registers based on interaction of AC-RoT and other devices
- Asserting / Deasserting βpayload_availableβ & βimage_activatedβ signals
OCP Recovery Interface Hardware Specifications
- Reference specifications for streaming boot sequence,
Caliptra Subsystem Streaming Boot Interface Hardware
Please refer to Caliptra subsystem Hardware specification.
Caliptra Subsystem Streaming Boot Sequence
- MCU ROM initializes the
PROT_CAP,DEVICE_STATUS,DEVICE_ID, andHW_STATUSregisters. Any other I3C initialization settings must be confirmed against I3C core specification. - Caliptra ROM updates the
PROT_CAPregister to set "Flashless boot (From RESET)", "FIFO CMS Support", and "Push-C-Image Support". - To start streaming boot, Caliptra ROM updates the
DEVICE_STATUSregister to "Recovery mode - ready to accept recovery imageβ for Device status byte (for the first image only) and "Flashless/Streaming Boot (FSB)" for Recovery reason codes. - Caliptra ROM updates the
RECOVERY_STATUSregister to "Awaiting Recovery Image" for Device recovery status and "image index" for Recovery image index. - BMC or a similar platform component sends an
INDIRECT_FIFO_CTRLwrite command with Component Memory Space (CMS) set to 0x0, reset set to 0x1, and image size set to streaming boot image size. - BMC or a similar platform component sends an
INDIRECT_FIFO_DATAwrite command. I3C device shall return a NACK response for any transfer that would cause the Head Pointer to advance to equal the Tail Pointer. BMC can implement flow control through NACK responses or by monitoring the FIFO space remaining via the Head and Tail Pointers. - The I3C device keeps head and tail pointers along with FIFO status up to date in the
INDIRECT_FIFO_STATUSregister. I3C streaming boot interface HW waits for an update toINDIRECT_FIFO_DATAwith exactly 256 bytes of data or less if remaining image data is less than 256 bytes. BMC could send multiple commands toINDIRECT_FIFO_DATAregister to fill the FIFO, but it must be a multiple of 4 bytes. The I3C device indicates payload availability only if the FIFO is full (256 bytes) or if BMC writes to image activation byte inRECOVERY_CTRLregister. - If
INDIRECT_FIFO_DATAhas 256B available to read, I3C device indicates data availability via side channel implemented as wirepayload_availableto Caliptra. For more information, see Requirement for payload available signal Implementation. Caliptra HW latches this wire into the register for Caliptra ROM/firmware to read. - If
payload_availableis asserted for the first time, Caliptra ROM readsINDIRECT_FIFO_CTRL_IMG_SIZEfor the image size. For eachpayload_availableassertion including the first one, Caliptra ROM continues to read the image data from theINDIRECT_FIFO_DATAregister until it finishes reading the data count specified as the image size. - Steps 8 and 9 repeat until all the image bytes are pushed over I3C and it matches the image size initialized in the
INDIRECT_FIFO_CTRL_IMG_SIZEregister. - When all the image bytes are received, Caliptra ROM sets
DEVICE_STATUSto "Recovery Pending (waiting for activation)". During image authentication,DEVICE_STATUSregister field device status remains "Recovery Pending (waiting for activation)". - After the above steps, Caliptra ROM/Firmware waits for BMC to activate the image. Image activation is indicated to Caliptra via either of the following methods:
- Side channel
image_activatedsignal. For more information, see Requirement for image activation signal implementation. - Caliptra ROM polls on
RECOVERY_CTRLregister for image activation status.
- When the image is activated, Caliptra ROM updates
RECOVERY_STATUSto βBooting recovery imageβ and moves to image authentication. - Irrespective of success or failure from image authentication (step 15 or 16), Caliptra ROM clears the image activation status before updating
RECOVERY_STATUS&DEVICE_STATUSregisters. - If the image activation is successful, and another recovery image stage is expected, then Caliptra RT firmware shall increment the βRecovery image indexβ in
RECOVERY_STATUSregister and set theRECOVERY_STATUSto indicate βAwaiting recovery imageβ (0x1). If no other stages are expected, then Caliptra RT firmware shall set theRECOVERY_STATUSto βRecovery successfulβ. Also, Caliptra ROM/Caliptra RT must update theDEVICE_STATUSregister to indicate "Running Recovery Image". - If the image consumption fails for any reason (for example, image authentication failure), Caliptra ROM/firmware updates
RECOVERY_STATUSregister to indicate appropriate recovery status andDEVICE_STATUSregister to "Fatal Error (Recover Reason Code not populated)". - BMC or a similar platform component sends the next image as requested in the image index, upon observing βAwaiting recovery imageβ in
RECOVERY_STATUSregister, and Caliptra RT FW and I3C HW go through the same flow as above.
Streaming Boot Sequence notes
- ROM refers to I3C registers by offset, and any change to the offset results in ROM failure or unexpected behavior.
- SoC is responsible for booting the I3C Core by configuring clock frequency/sampling rate with the following registers and any other requirements specified in I3C core specifications.
- SoC is responsible for assigning static addresses to the I3C Core.
- Caliptra ROM/runtime firmware updates the
RECOVERY_STATUSwith the recovery image index.- For recovery image index:
0x0: Caliptra firmware0x1: SoC Manifest0x2: MCU firmware
- For recovery image index:
- SoC/MCU ROM sets the
HW_STATUSregister per recovery spec at any time based on SoC-specific conditions. - MCU ROM programs
DEVICE_IDregister value based on associated fuse values. - SoC/MCU ROM initializes the
PROT_CAP,DEVICE_ID, andDEVICE_STATUSregisters. - Caliptra ROM performs only read-modify-write (RMW) for updates to registers.
- I3C device must update FIFO size (1-256 Byte), Max transfer size and type of region (tie this to 0x1) to
INDIRECT_FIFO_STATUSregister, which could be read by BMC firmware to understand the size of the FIFO and max transfer size. - BMC is responsible for re-sending the transaction if it fails due to PEC error.
- ALL Caliptra ROM accesses to recovery interface are completed via DMA assist.
Requirement for Payload Available Signal Implementation
- Name:
payload_available - Type: 1 bit wire
- Source: Recovery Interface / I3C Core
- Destination: Caliptra core
Assertion
- The
payload_availablesignal must assert under either of the following conditions:- If the recovery FIFO indicates full (256 bytes).
- If the image activation status is asserted and the recovery FIFO indicates not empty for the last transfer.
De-assertion
- The
payload_availablesignal must reset if recovery FIFO indicates empty.
Requirement for Image Activation Signal Implementation
- Name:
image_activated - Type: 1 bit wire
- Source: Recovery Interface
- Destination: Caliptra core
Assertion
- The
image_activatedsignal must assert whenRECOVERY_CTRLregister byte 2 has value0xf(indicating image activation).
De-assertion
- The
image_activatedsignal must deassert when theRECOVERY_CTRLregister byte 2 has value0x0(indicating image activation is cleared).
Platform component requirements for recovery support
- The BMC, or a similar platform, should not send payload to streaming boot interface (/I3C target) device if
RECOVERY_CTRLregister has byte 2 indicating Image Activated. BMC must wait to clear the byte 2. (Streaming boot interface is responsible for clearing this byte by writing 1). - The BMC, or a similar platform, sends payload to I3C target device in chunks of 256 bytes (header (4B) + FW bytes(256B) as I3C target transfer) only, unless it is the last write for the image. Before sending the payload, it reads FIFO empty status from
INDIRECT_FIFO_STATUSregister. - After the last write for the image, the BMC, or a similar platform, activates the image.
Life Cycle Controller & SoC Debug Architecture
Please refer to Caliptra subsystem hardware specification.
OCP L.O.C.K. HW Architecture and Security Claims
Please refer to Caliptra and Caliptra subsystem hardware specification for HW implementation details and Key Vault (KV) rules.
Security Claims and Assumptions
- The OCPβ―LOCK implementation employs NIST approved algorithms (e.g., AES, SHAβ2/HMAC)
- Certain secret keys involved in OCPβ―LOCK (HEK/HEK_SEED, deβobf key, MEK, etc.) are never readable by firmware and are only usable by cryptographic engines or DMA via KV references. KV entries carry lock bits (lockβwrite, lockβuse) that enforce nonβobservability and immutability until uC reset.
- If Caliptra runtime firmware (RT FW) is compromised, it can generate an arbitrary new MEK and release it via the allowed DMA path; this does not break confidentiality of media encrypted under earlier MEKs, because ciphertext written prior to FW compromise remains bound to different keys.
- MEK generation flow is not isolated from RT FW. MEK load flow is protected from RT FW and this FW can generate a new MEK (e.g., for rotation) but cannot reβmaterialize a previous MEK except by executing the OCPβ―LOCK MEK load flow under ROMβenabled KV rules and secrets. Recovering a MEK from its wrapped/encrypted form uses LOCKβscoped KV slots, filtering, and AES writeβpath policy; the unwrap/decryption outputs to the designated KV slot, not to FW.
- DMA offset used to send MEK is locked when the FUSE_WR_DONE is set to high and cannot be changed until the next cold boot.
- Caliptra subsystemβs security boundary ends at the Caliptra core DMA write of MEK into the required destination (offset determined through strap by integrator); confidentiality/integrity beyond that destination is the SoC ownerβs responsibility.
- KV rules ensure that a KV slot can be used as AES key but cannot be used as AES plaintext or ciphertext, except for MEK encryption KV slot which has its own unique rule.
- ROM sequencing & enablement: Caliptra ROM sets OCP_LOCK_IN_PROGRESS before any RT FW OCP LOCK operation, which enables the KV filtering/isolation logic and the Key Release path; if not set, OCP LOCK data paths are disabled. If the OCP LOCK is intended to be used, Caliptra ROM MUST set the OCP_LOCK_IN_PROGRESS to high.
- The integrator sets OCP_LOCK enable strap pin (a HW tieoff pin, not modifiable by SW) at integration time and Caliptra ROM sets OCP_LOCK_IN_PROGRESS before any OCP LOCK operation, which enables the KV Slot filtering/isolation logic and the Key Release path; if not set, OCP LOCK data paths are disabled. This rule also ensures that any potential security issue comes from OCP LOCK is under the quarantine of OCP LOCK boundary and does not affect Caliptraβs rest of KV slots. However, KV 16 has a different rule because it is populated by Caliptra ROM, which sets OCP_LOCK_IN_PROGRESS bit. Thus, KV 16 is restricted with OCP_LOCK_ENABLE strap pin but not by OCP_LOCK_IN_PROGRESS.
- While OCPβ―LOCK is in progress, AES operations cannot be abused by malicious FW to perform arbitrary AES operation into KV to exfiltrate or implant-control data if it is not for MEK decryption process and MEK decryption KV slots. KV writeβpath restrictions are enforced in HW: mode check + key/slot policy; destination is forced to a designated LOCK slot; other AES ops route only to FW dataβout, not KV; writeβenable hardened to prevent midβtransfer changes.
- DMA data FIFOs are flushed after every DMA transfer, ensuring that there is no residue data remaining from MEK after the OCP LOCK flow completion
- Caliptra top enforces mutual exclusion (BUSY checks / HW_ERROR) while LOCK flows are active; KV rules enforce single active engine for read/write correctness.
- Any KV slot that stores a secret value can be partially overwritten by the HMAC core, since HMAC writes 384 bits of a 512-bit slot. However, the HMAC core always sets the last valid d-word flag when performing this partial overwrite. This flag ensures that any remaining 128 bits in the slot are treated as invalid, preventing an adversary from exploiting the unchanged portion for pre-image recovery. If the last valid d-word flag is set for 384-bit and HMAC is triggered for 512-bit, HMAC core generates an error signal. Similarly, AES cannot perform arbitrary encryption or decryption using partially overwritten KV slots because AES keys are at most 256 bits and always start from the least significant bits (LSB) of the slot. Since HMAC writes a minimum of 384 bits, any AES operation will necessarily use the updated portion of the slot, not any stale data. The same KV slot may also be reused for ML-KEM or ECC operations. These are asymmetric cryptographic operations where the secret is either a private key or a seed used to derive the private key. Partial overwrites do not expose these secrets. While it could be argued that such secrets might be manipulated to sign arbitrary data or perform unauthorized decapsulation, these operations can only occur after ROM executionβat which point Caliptra secrets are either cleared or locked, eliminating this risk. OCP LOCK utilizes ML-KEM and ECDH for the key wrapping operations however these operations do not go through KV and managed by RT FW and thus these are out of scope of this partial write feature.
- MEK, HEK seed, MEK encryption key cannot be copied. This is an important isolation guarantee.
- Single-encrypted ("obfuscated") MEK is exposed to RT FW of Caliptra. If malicious firmware gains access to the obfuscated MEK it can successfully derive the MEK to KV23 and release it to the SSD controller without requiring any other security inputs (Access Keys, MPK, EPK, etc). Firmware must clear it from memory immediately after use.
Terminology
The following acronyms and abbreviations are used throughout this document.
References
- NIST Special Publication 800-193 Platform Firmware Resiliency Guidelines
- Global Platform Technology Root of Trust Definitions and Requirements Version 1.1 Public
- Open Compute Project Secure Boot Specification
- TCG DICE Layering Architecture Version 1.0 Revision 0.19 July 23, 2020
- TCG DICE Attestation Architecture Version 1.00 Revision 0.23 March 1, 2021
- TCG Hardware Requirements for a Device Identifier Composition Engine Family β2.0β Level
- Side-Channel Attacks: Ten Years After Its Publication and the Impacts on Cryptographic Module Security Testing
- Attestation of System Components v1.0 Requirements and Recommendations
- OCP Security WG: Ownership Transfer
Acknowledgments
The Caliptra Workgroup acknowledges the following individuals for their contributions to this specification.
CONTRIBUTORS
- Akash Singh (NVIDIA)
- Alex Tzonkov (AMD)
- AndrΓ©s Lagar-Cavilla (Google)
- Anjana Parthasarathy (Microsoft)
- Anthony Rocha (AMD)
- Avirup Mullick (NVIDIA)
- Bharat Pillilli (Microsoft)
- Bryan Kelly (Microsoft)
- Caleb Whitehead (Microsoft)
- Clayton Kuchta (Microsoft)
- Emre Karabulut (Microsoft)
- Howard Tran (Google)
- James Zhang (NVIDIA)
- Jeff Andersen (Google)
- John Traver (AMD)
- Jordan Hand (Google)
- Kiran Upadhyayula (Microsoft)
- Kor Nielsen (Google)
- Louis Ferraro (AMD)
- Marius Schilder (Google)
- Matt Cockrell (Google)
- Michael Norris (Microsoft)
- Mojtaba Bisheh Niasar (Microsoft)
- Nathan Nadarajah (AMD)
- Nilesh Patel (Microsoft)
- Norman Stewart (AMD)
- Paul Chou (NVIDIA)
- Piotr Kwidzinski (AMD)
- Prabhu Jayana (AMD)
- Rob Strong (AMD)
- Sudhir Mathane (AMD)
- Stephanie Morton (Google)
- Steven Bellock (NVIDIA)
- Varun Sampath (NVIDIA)
- Vishal Soni (Microsoft)
- Vishal Mhatre (Microsoft)
Footnotes
Caliptra is Spanish for βroot capβ and describes the deepest part of the root.
This obfuscation key may be a chip-class secret, or a chip-unique PUF, with the latter preferred.
This memory should only be volatile in a power loss event. See details in the reset flow section.
When a hitless update occurs, and then following reset, Caliptra shall execute the updated firmware and shall maintain the measurements that it collected during boot. Caliptra shall support the reporting of these measurements with signed attestations. Hitless update of Caliptraβs FMC shall not be supported. Hitless update requires creating a new DICE identity, which would require access to IDevID and LDevID. Retention of IDevID and LDevID (privkeys) during post-boot introduce a security vulnerability.
Upon boot, firmware defensively treats existing SRAM contents as potentially malicious, emplaced by prior firmware with a vulnerability.
The format of this log is outside the scope of this specification.
The format of this blob is outside the scope of this specification.
ECDSA and MLDSA-87 is used for firmware verification and SPDM (signing).
SHA is used with ECDSA and HMAC, and is also used to generate measurements.
64ed6c3
Version History
| Date | Version | Description |
|---|---|---|
| September 2025 | 1.0 | Initial release |
| [TBD] | [TBD] | Minor formatting and grammar fixes. Change KDF labels. Rotate HPKE keypairs on warm reset as well as firmware-update reset. Add response flag to REPORT_HEK_METADATA command. Rename "Opal C_PIN" to "TCG C_PIN". Rename REPORT_EPOCH_KEY_STATE to GET_EPOCH_KEY_STATE. Illustrate usage of the internal DRBG for drive-firmware-specific use-cases. Deprecate the "preconditioned key extract" construct, and instead combine keys using SP 800-108 KDFs. Update HPKE labels to match IETF. |
License
Open Web Foundation (OWF) CLA
Contributions to this Specification are made under the terms and conditions set forth in Modified Open Web Foundation Agreement 0.9 (OWFa 0.9). (As of October 16, 2024) (βContribution Licenseβ) by:
- Microsoft
- Samsung
- Kioxia
- Solidigm
Usage of this Specification is governed by the terms and conditions set forth in Modified OWFa 0.9 Final Specification Agreement (FSA) (As of October 16, 2024) (βSpecification Licenseβ).
You can review the applicable Specification License(s) referenced above by the contributors to this Specification on the OCP website at https://www.opencompute.org/contributions/templates-agreements.
For actual executed copies of either agreement, please contact OCP directly.
NOTWITHSTANDING THE FOREGOING LICENSES, THIS SPECIFICATION IS PROVIDED BY OCP "AS IS" AND OCP EXPRESSLY DISCLAIMS ANY WARRANTIES (EXPRESS, IMPLIED, OR OTHERWISE), INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, OR TITLE, RELATED TO THE SPECIFICATION. NOTICE IS HEREBY GIVEN, THAT OTHER RIGHTS NOT GRANTED AS SET FORTH ABOVE, INCLUDING WITHOUT LIMITATION, RIGHTS OF THIRD PARTIES WHO DID NOT EXECUTE THE ABOVE LICENSES, MAY BE IMPLICATED BY THE IMPLEMENTATION OF OR COMPLIANCE WITH THIS SPECIFICATION. OCP IS NOT RESPONSIBLE FOR IDENTIFYING RIGHTS FOR WHICH A LICENSE MAY BE REQUIRED IN ORDER TO IMPLEMENT THIS SPECIFICATION. THE ENTIRE RISK AS TO IMPLEMENTING OR OTHERWISE USING THE SPECIFICATION IS ASSUMED BY YOU. IN NO EVENT WILL OCP BE LIABLE TO YOU FOR ANY MONETARY DAMAGES WITH RESPECT TO ANY CLAIMS RELATED TO, OR ARISING OUT OF YOUR USE OF THIS SPECIFICATION, INCLUDING BUT NOT LIMITED TO ANY LIABILITY FOR LOST PROFITS OR ANY CONSEQUENTIAL, INCIDENTAL, INDIRECT, SPECIAL OR PUNITIVE DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO THIS SPECIFICATION, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND EVEN IF OCP HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Acknowledgements
The Contributors of this Specification would like to acknowledge the following:
- AndrΓ©s Lagar-Cavilla (Google)
- Amber Huffman (Google)
- Carl Lundin (Google)
- Charles Kunzman (Google)
- Chris Sabol (Google)
- Jeff Andersen (Google)
- Srini Narayanamurthy (Google)
- Zach Halvorsen (Google)
- Anjana Parthasarathy (Microsoft)
- B Keen (Microsoft)
- Bharat Pillilli (Microsoft)
- Bryan Kelly (Microsoft)
- Christopher Swenson (Microsoft)
- Eric Eilertson (Microsoft)
- Lee Prewitt (Microsoft)
- Michael Norris (Microsoft)
- Eric Hibbard (Samsung)
- Gwangbae Choi (Samsung)
- Jisoo Kim (Samsung)
- Michael Allison (Samsung)
- Festus Hategekimana (Solidigm)
- Gamil Cain (Solidigm)
- Scott Shadley (Solidigm)
- Fred Knight (Kioxia)
- James Borden (Kioxia)
- John Geldman (Kioxia)
- Paul Suhler (Kioxia)
- Artem Zankovich (Micron)
- Bharath Madanayakanahalli Gururaj (Micron)
- Jef Munsil (Micron)
- Jimmy Ruane (Micron)
- Jonathan Chang (Micron)
- Rob Strong (Micron)
Compliance with OCP Tenets
Openness
OCP L.O.C.K. source for RTL and firmware will be licensed using the Apache 2.0 license. The specific mechanics and hosting of the code are work in progress due to CHIPS alliance timelines. Future versions of this spec will point to the relevant resources.
Efficiency
OCP L.O.C.K. is used to generate and load keys for use of encrypting user data prior to storing data at rest and decrypting stored user data at rest when read. So, it cannot yield a measurable impact on system efficiency.
Impact
OCP L.O.C.K. enables consistency and transparency to a foundational area of security of media encryption keys such that no drive firmware in the device ever has access to a media encryption key. Furthermore, no decrypted media encryption key exists in the device when power is removed from the device.
Scale
OCP L.O.C.K. is a committed intercept for storage products for Google and Microsoft. This scale covers both a significant portion of the hyperscale and enterprise markets.
Sustainability
The goal of OCP L.O.C.K. is to eliminate the need for cloud providers to destroy storage devices (e.g., SSDs) by providing a mechanism that increases the confidence that a media encryption key within the device is deleted during cryptographic erase. This enables repurposing the device and or components on the device at end of use or end of life. Given the size of the market serving cloud providers, this provides a significant reduction of e-waste.
Base specification
Introduction
OCP L.O.C.K. (Layered Open-source Cryptographic Key management) provides secure key management for Data-At-Rest protection in self-encrypting storage devices.
OCP L.O.C.K. was originally created as part of the Open Compute Project (OCP). The major revisions of the OCP L.O.C.K. specifications are published as part of Caliptra at OCP, as OCP L.O.C.K. is an extension to Caliptra. The evolving source code and documentation for Caliptra are in the repository within the CHIPS Alliance Project, a Series of LF Projects, LLC.
OCP L.O.C.K. may be integrated within a variety of self-encrypting storage devices, and is not restricted exclusively to NVMeβ’. OCP L.O.C.K. is designed for compatibility with the TCG Storage Architecture TCG Storage Architecture Core Specification, including support for TCG Opal1 TCG Storage Security Subsystem Class: Opal Specification, Enterprise TCG Storage Security Subsystem Class: Enterprise, and Key Per I/O TCG Storage Security Subsystem Class (SSC): Key Per I/O specifications.
Opal in the context of this document includes the Opal Family of SSCs: Opal SSC, Opalite SSC, Ruby SSC. Pyrite is excluded as Pyrite SSDs are non-SED.
Background
In the life of a storage device in a datacenter, the device leaves the supplier, a customer writes user data to the device, and then the device is decommissioned. Customer data is not allowed to leave the data center. The cloud provider needs high confidence that the storage device leaving the datacenter is secure. The current default cloud provider policy to ensure this level of security is to destroy the drive. Other policies may exist that leverage drive capabilities (e.g., cryptographic erase), but are not generally deemed inherently trustworthy by these cloud providers Self-encrypting deception: weaknesses in the encryption of solid state drives. This produces significant e-waste and inhibits any re-use/recycling.
Self-encrypting drives (SEDs) store data encrypted at rest using media encryption keys (MEKs). SEDs include the following building blocks:
- The storage media that holds data at rest.
- An encryption engine which performs line-rate encryption and decryption of data as it enters and exits the drive.
- A drive controller which exposes host-side APIs for managing the lifecycle of MEKs.
MEKs may be bound to user credentials, which the host must provide to the drive in order for the associated data to be readable. A given MEK may be bound to one or more credentials. This model is captured in the TCG Opal and Enterprise specifications.
MEKs may be erased, to cryptographically erase all data which was encrypted to the MEK. To erase an MEK, it is sufficient for the drive to erase all copies of it, or all copies of a key with which it was protected.
Threat model
The protected asset is the user data stored at rest on the drive. The adversary profile extends up to nation-states in terms of capabilities.
Adversary capabilities include:
- Interception of a storage device in the supply chain.
- Theft of a storage device from a data center.
- Destructively inspecting a stolen device.
- Running arbitrary firmware on a stolen device.
- This includes attacks where vendor firmware signing keys have been compromised.
- Attempting to glitch execution of code running on general-purpose cores.
- Stealing debug core dumps or UART/serial logs from a device while it is operating in a data center, and later stealing the device.
- Gaining access to any class secrets, global secrets, or symmetric secrets shared between the device and an external entity.
- Executing code within a virtual machine on a multi-tenant host offered by the cloud provider which manages an attached storage device.
- Accessing all device design documents, code, and RTL.
Given this adversary profile, the following are a list of vulnerabilities that OCP L.O.C.K. is designed to mitigate.
- MEKs managed by storage drive firmware are compromised due to implementation bugs or side-channels.
- MEKs erased by storage drive firmware are recoverable via invasive techniques.
- MEKs are not fully bound to user credentials due to implementation bugs.
- MEKs are bound to user credentials which are compromised by a vulnerable host.
- Cryptographic erase was not performed properly due to a buggy host.
OCP L.O.C.K. goals
OCP L.O.C.K. is being defined to improve drive security. In an SED that integrates Caliptra with OCP L.O.C.K. features enabled, Caliptra will act as a Key Management Block (KMB). The KMB will be the only entity that can read MEKs and program them into the SED's encryption engine. The KMB will expose services to drive firmware which will allow the drive to transparently manage each MEK's lifecycle, without being able to access the raw MEK itself.
The OCP L.O.C.K. KMB satisfies the following properties:
- Prevents leakage of media keys via firmware vulnerabilities or side-channels, by isolating MEKs to a trusted component.
- Binds MEKs to a given set of externally-supplied access keys, provisioned with replay-resistant transport security such that they can be injected without trusting the host.
- Uses epoch keys for attestable epoch-based cryptographic erase.
Non-goals
The following areas are out of scope for this project.
- Compliance with IEEE 1619^TM^-2025 IEEE Draft Standard for Cryptographic Protection of Data on Block-Oriented Storage Devices requirements around key scope, i.e. restricting a given MEK to only encrypt a maximum of 2^44^ 128-bit blocks. Drive firmware will be responsible for enforcing this.
- Compliance with Common Criteria requirement FCS_CKM.1.1(c) collaborative Protection Profile for Full Drive Encryption - Encryption Engine when supporting derived MEKs. Key Per I/O calls for DEKs to be injected into the device. To support OCP L.O.C.K.'s goals around enabling cryptographic erase, before the injected DEK is used to encrypt user data, the DEK is first conditioned with an on-device secret that can be securely zeroized. FCS_CKM.1.1(c) currently does not allow injected keys to be thus conditioned and therefore does not allow for cryptographic erase under the Key Per I/O model. A storage device that integrates OCP L.O.C.K. and aims to be compliant with this Common Criteria requirement may not support Key Per I/O.
- Authorization for EPK/DPK/MPK rotation, or binding a given MEK to a particular locking range. Drive firmware is responsible for these.
- Mitigation against availability attacks carried out by attackers with physical presence.
Integrating OCP L.O.C.K.
OCP L.O.C.K. is a feature set conditionally compiled into Caliptra Subsystem 2.1+. It consists of functionality in Caliptra ROM and Runtime Firmware along with a hardware interface from Caliptra Core to a vendor-implemented encryption engine, which is the path by which Caliptra Core programs MEKs. This hardware interface leverages the AXI Manager which is only available in Caliptra Subsystem.
OCP L.O.C.K.'s KMB offers cryptographic services for drive firmware, which implements a host-facing storage interface, such as TCG Opal, Enterprise, or Key Per I/O. The host will interact with drive firmware to configure the storage device and actuate storage lifecycle events, such as MEK loading / unloading and cryptographic erase. Certain of these lifecycle events are handled by KMB on behalf of drive firmware.
In Caliptra Subsystem, drive firmware runs within the Manufacturer Control Unit (MCU) Caliptra 2.0 Subsystem Specification.
Integration verification
A product that integrates OCP L.O.C.K. will be expected to undergo an OCP S.A.F.E. review to ensure that L.O.C.K. is correctly integrated and that drive firmware correctly invokes L.O.C.K. services.
Architecture
{#architecture-diagram}
To safeguard user data stored on the drive, KMB defines a set of "protection keys", each of which must be available in order for an MEK to be accessible.
-
The data protection key (DPK), which is managed by the nominal owner of the data. A given MEK is bound to a single DPK.
- In Opal or Enterprise the DPK may be protected by the user's C_PIN, while in Key Per I/O the DPK may be the DEK associated with a given key tag.
-
A configurable number of Multi-party Protection Keys (MPKs), which are each managed by an additional entity that must assent before user data is available. A given MEK may be bound to zero or more MPKs.
- Each multi-party entity grants access to the data by providing an access key to the drive. Each MPK is protected by a distinct access key, which is never stored persistently within the drive. MPK access keys are protected in transit using HPKE Hybrid Public Key Encryption. This enables use-cases where the access key is served to the drive from a remote key management service, without revealing the access key to the drive's host.
- Binding an MEK to zero MPKs allows for legacy Opal, Enterprise, or Key Per I/O support.
-
A composite epoch protection key (EPK), which is the output of a KDF of two "component epoch keys" held within the device: the Soft Epoch Key (SEK) and the Hard Epoch Key (HEK). The EPK is managed by the storage device itself, and all MEKs in use by the device are bound to it.
- All MEKs in use by the drive can be cryptographically erased by zeroizing either the SEK or HEK. New MEKs shall not be loadable until the zeroized epoch keys are regenerated.
- KMB reports the zeroization state of the SEK and HEK, and therefore whether the drive is in a cryptographically erased state.
- The SEK is managed by drive firmware and shall be held in rewritable storage, e.g. in flash memory.
- The HEK is derived from a seed held in fuses and is only visible to KMB hardware. This provides assurance that an advanced adversary is unable to recover key material that had been in use by the drive prior to HEK seed zeroization.
The EPK, DPK, and set of configured MPKs are used together to derive an MEK secret, which protects a given MEK. The MEK protection is implemented as one of two methods:
-
MEK encryption - the drive obtains a random MEK from KMB, encrypted by two keys: the MEK secret and the MEK Deobfuscation Key (MDK), and is allowed to load that wrapped MEK into the encryption engine via KMB. This supports Opal or Enterprise use-cases.
-
MEK derivation - the drive instructs KMB to derive an MEK from the MEK secret and load the MEK into the encryption engine. This may support either Opal, Enterprise, or Key Per I/O use-cases.
MEKs are never visible to drive firmware. Drive firmware instructs KMB to load MEKs into the key cache of the encryption engine, using standard interfaces described in Section. Each MEK has associated vendor-defined metadata, e.g. to identify the namespace and LBA range to be encrypted by the MEK.
KMB shall not cache MEKs in memory. The encryption engine shall remove all MEKs from the encryption engine on a power cycle or upon request from drive firmware.
All keys randomly generated by KMB are generated using a DRBG seeded by Caliptra's TRNG. The DRBG may be updated using entropy provided by the host.
Key hierarchy
{#key-hierarchy}
Table enumerates the CSPs (Critical Security Parameters) used by KMB, along with where each CSP is held in the Key Vault (KV). KV slots 16-23 are reserved for OCP L.O.C.K.
Table: Critical Security Parameters {#critical-security-parameters}
| CSP | Usage | Lifecycle | KV slot number |
|---|---|---|---|
| UDS | Used to derive the DICE IDevID CDI, which in turn is used to derive the MDK and HEK. See Figures [-Figure] and [-Figure]. | Held in the Key Vault during cold reset and zeroized after use. Rendered irrecoverable if zeroized from persistent storage. | 0 |
| MDK | Used to perform the inner encryption and decryption of each MEK. See Section. | Derived from the UDS during cold reset. Held in the Key Vault and lost on cold reset. Rendered irrecoverable if the DICE UDS is zeroized from persistent storage. | 16 |
| HEK seed | Contributes to the derivation of the HEK. See Section. | Randomly generated and stored in fuses. Drive firmware zeroizes the HEK seed from fuses on demand, according to the constraints given in Section. | N/A |
| HEK | Contributes to the derivation of the EPK. See Figure for one of several flows where this is done. | Derived from the HEK seed and UDS during cold reset. Held in the Key Vault and lost on cold reset. Rendered irrecoverable if the HEK seed or the DICE UDS are zeroized from persistent storage. | 22 |
| SEK | Contributes to the derivation of the EPK. See Figure for one of several flows where this is done. | Managed outside of Caliptra. Randomly generated by drive firmware and stored in flash. When held by Caliptra, the SEK is held in volatile memory and is zeroized after each use. Drive firmware zeroizes the SEK from flash on demand, according to the constraints given in Section. | N/A |
| EPK | Contributes to the derivation of each Locked MPK encryption key. See Figure for one of several flows where this is done. Contributes to the derivation of each MEK secret. See Figure. | Derived from the HEK and SEK. Held in the Key Vault and zeroized after each use. Rendered irrecoverable if the SEK, HEK seed, or DICE UDS are zeroized from persistent storage. | 17 |
| Volatile Escrow Key (VEK) | Allows KMB firmware to escrow volatile secrets for future access by runtime KMB firmware. Encrypts/decrypts Enabled MPKs. See Figures [-Figure] and [-Figure]. | Randomly generated when first needed at runtime. Held in the Key Vault and lost on cold reset. | 18 |
| HPKE private keys | Provides transport encryption for injected access keys. See Section. | Randomly generated on startup. Held in volatile memory, rotated on demand, and lost on cold reset. | N/A |
| Access keys | Contributes to the derivation of a Locked MPK encryption key. See Figure for one of several flows where this is done. | Managed outside of Caliptra. Asymmetrically encrypted using the HPKE public key. When held by Caliptra, each access key is held in volatile memory and is zeroized after each use. | N/A |
| Locked MPK encryption keys | Encrypts/decrypts a Locked MPK. See Figure for one of several flows where this is done. | Derived from the EPK and an injected access key. Held in the Key Vault and zeroized after each use. Rendered irrecoverable if the SEK, HEK seed, or DICE UDS are zeroized from persistent storage. | 19 |
| MPKs | Contributes to the derivation of an MEK secret. See Figure. | Randomly generated on demand. Encrypted with a Locked MPK encryption key or the VEK (depending on whether the MPK is locked or enabled). When decrypted, each MPK is held in volatile memory and is zeroized after each use. Rendered irrecoverable if the SEK, HEK seed, or DICE UDS are zeroized from persistent storage. | N/A |
| MPK secrets | Contributes to the derivation of an MEK secret. See Figure. | Derived from a sequence of MPKs. When held by Caliptra, the MPK secret is held in the Key Vault and is zeroized after an MEK secret is derived. | 20 |
| DPKs | Contributes to the derivation of an MEK secret. See Figure. | Managed outside of Caliptra. When held by Caliptra, the DPK is held in volatile memory and is zeroized after each use. | N/A |
| MEK secrets | Either encrypts/decrypts an MEK or is used to derive an MEK seed. See Sections [-Section] and [-Section]. | Derived from the EPK, DPK, and zero or more decrypted MPKs. Held in the Key Vault and zeroized after each use. Rendered irrecoverable if the SEK, HEK seed, or DICE UDS are zeroized from persistent storage. | 21 |
| MEK seeds | Used to derive an MEK. See Section. | Derived from an MEK secret. Held by firmware and zeroized after each use. Rendered irrecoverable if the SEK, HEK seed, or DICE UDS are zeroized from persistent storage. | N/A |
| MEKs | Encrypts/decrypts user data written to the storage device. See Section. | Either randomly generated on demand and encrypted at rest with the MEK secret and MDK, or derived from the MEK secret. Held in the Key Vault and zeroized after programming to the Encryption Engine. Rendered irrecoverable if the SEK, HEK seed, or DICE UDS are zeroized from persistent storage. | 232 |
Randomly-generated MEKs are held in memory rather than the Key Vault when they are first generated. See Table for more details.
Key Vault
Some elements of the OCP L.O.C.K. key hierarchy are held in the Caliptra Key Vault. Such keys are wielded directly by hardware and cannot be read by firmware.
Other elements of the key hierarchy are not thus protected, and are held in KMB's memory instead. Table describes why each such CSP is held outside of the Key Vault.
Table: CSPs visible to KMB firmware {#fw-visible-csps}
| Firmware-visible CSP | Reason for being visible to KMB firmware |
|---|---|
| SEK | Managed by drive firmware, passed in via mailbox command. |
| DPKs | Managed by drive firmware, passed in via mailbox command. |
| HPKE private keys | To mitigate side-channel attacks, the ECDH and ML-KEM engines mandate that any operation whose source includes a key from the Key Vault must place the result back in the Key Vault. If the HPKE private keys were held in the Key Vault, then the ECDH and ML-KEM Decaps results would be held in the Key Vault as well. Section illustrates that these results are used to compute an HMAC message, a SHA3 digest, and an AES-GCM IV. Caliptra's Key vault hardware does not presently support these operations. Therefore the ECDH and ML-KEM Decaps results cannot be held in the Key Vault; ergo, the HPKE private keys also cannot be held in the Key Vault. |
| Access keys | Each access key is encrypted using an HPKE private key. Since firmware manages the HPKE private keys, it is not meaningful to decrypt the access key into the Key Vault. |
| Decrypted MPKs | MPKs are encrypted at rest. The encryption key for a given MPK may be periodically rotated. To effectuate this, the MPK is decrypted using the old encryption key, and re-encrypted with the new encryption key. The AES engine does not presently support using keys in the Key Vault as messages to encrypt. Therefore, MPKs must be decrypted into memory when their encryption keys are rotated. |
| MEK seeds (only used for derived MEKs) | See Section for rationale on why the MEK seed is readable by firmware. |
| Random MEKs (upon initial generation) | Caliptra does not presently support a data path between its DRBG and its AES engine. Therefore, when a random MEK is first generated, KMB firmware reads the random MEK from the DRBG, and then encrypts it with the MDK (inner layer) and MEK secret (outer layer). See Section for additional details on the motivation for the inner layer of MDK encryption. |
All keys held by KMB firmware are protected by Caliptra, and cannot be viewed by drive firmware.
Key Derivation
In this specification, several flows involve deriving keys via a key derivation function (KDF). This specification leverages the KDF in Counter Mode defined in NIST SP 800-108 Recommendation for Key Derivation Using Pseudorandom Functions (Rev. 1) section 4.1, where the PRF is HMAC512. As each key produced by this KDF is no larger than 512 bits, a single invocation of the underlying PRF is needed. Therefore, each instance of `SP 800-108 KDF` in subsequent flows deconstructs to the following operation:
\(K_{OUT}\) = HMAC512(\(K_{IN}\), 0x01 || Label [ || 0x00 || Context])
There is one exception to this in Section, where CMAC instead of HMAC is used as the PRF for a particular instance of KDF. See Section for details.
Preconditioned AES
In this specification, several flows involve deriving a key for use in AES-GCM. In AES-GCM it is critical that IV collisions be avoided. To that end SP 800-38D Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC section 8.3 stipulates that AES-GCM-Encrypt may only be invoked at most \(2^{32}\) times with a given AES key. One approach to address this constraint is to maintain counters to track the usage count of a given key. Such tracking would be difficult for Caliptra, which lacks direct access to rewritable storage which would be needed to maintain a counter that preserves its value across power cycles.
To elide the need for maintaining counters, this specification defines the "preconditioned AES-Encrypt" and "preconditioned AES-Decrypt" operations, illustrated in Figures [-Figure] and [-Figure]. These operations are similar in principle to XAES-256-GCM The XAES-256-GCM extended-nonce AEAD.
{#preconditioned-aes-encrypt}
{#preconditioned-aes-decrypt}
Each encryption key is preconditioned using a randomly-generated 96-bit salt to compute a subkey, which is used with AES-GCM-Encrypt. This ensures that it is safe to use a given input encryption key in approximately \(2^{80}\) preconditioned AES-Encrypt operations before an IV collision is expected to occur with probability greater than \(2^{-32}\). The \(2^{80}\) limit is large enough that a given storage device will experience hardware failure well before that limit is reached. Ergo, usage counters within the drive are not needed for these keys. See [Appendix Section] for additional details on the calculations behind this figure.
In addition to a plaintext message, this operation supports optional metadata for the message, which is attached as AAD.
VEK
The Volatile Escrow Key (VEK) is held in the Key Vault and is used to protect volatile keys held outside of the Key Vault, specifically Enabled MPKs.
The VEK is lazily generated upon first use and is lost when the drive shuts down. Lazy generation allows injected host entropy to contribute to the key's generation.
The VEK is randomly generated and held in a Key Vault slot. There does not exist a hardware data path directly between Caliptra's DRBG and Key Vault. Therefore, KMB firmware will read random data from the DRBG and send it to the Key Vault. To prevent KMB firmware from seeing the actual VEK, the randomness obtained by firmware is permuted using the HEK, as illustrated in Figure.
{#vek-generation}
MPKs
Multi-party Protection Keys (MPKs) are the mechanism by which KMB enforces multi-party authorization as a precondition to loading an MEK. MPKs exist in one of two states: locked or enabled. In both these states the MPK is encrypted to a secret known only to KMB.
- A Locked MPK's encryption key is derived from the HEK, SEK, and an externally-supplied access key to which the MPK is bound. The Locked MPK is held at rest by drive firmware.
- An Enabled MPK's encryption key is a common volatile secret held within KMB which is randomly generated and is lost when the drive shuts down. See Section for details on how this key is generated. Enabled MPKs are held in drive firmware memory.
The externally-supplied access key is encrypted in transit using an HPKE public key held by KMB. The "enabled" state allows the HPKE keypair to be rotated after the access key has been provisioned to the storage device, without removing the ability for KMB to decrypt the MPK when later loading an MEK bound to that MPK.
For each MPK to which a given MEK is bound, the host is expected to supply the MPK's encrypted access key to drive firmware via a host-facing API. Upon receipt, drive firmware passes that encrypted access key to KMB, along with the Locked MPK, to produce the Enabled MPK which is cached in drive memory. This is performed once for each MPK to which the MEK is bound, prior to drive firmware instructing KMB to program the MEK. See Section for details on how host-facing APIs map to KMB mailbox commands.
Transport encryption for MPK access keys
KMB maintains a set of HPKE keypairs, one per HPKE algorithm that KMB supports. Each HPKE public key is endorsed with a certificate that is generated by KMB and signed by Caliptra's DICE TCG DICE identity. HPKE keypairs are randomly generated on KMB startup, and mapped to unique handles. Keypairs may be periodically rotated. Drive firmware is responsible for enumerating the available HPKE public keys and exposing them to the user via a host interface. See Section for how the public keys may be presented to the host.
When a user wishes to generate or enable an MPK (which is required prior to loading any MEKs bound to that MPK), the user performs the following steps:
- Select the HPKE public key and obtain its certificate from the storage device.
- Validate the HPKE certificate and attached DICE certificate chain.
- Decode the HPKE capabilities of the storage device based on the HPKE certificate.
- Seal their access key using the HPKE public key.
- Transmit the sealed access key to the storage device.
Upon receipt, KMB will unwrap the access key and proceed to generate or enable the MPK.
Upon Caliptra cold reset or firmware-update reset, the HPKE keypairs are autonomously regenerated. See Section.
HPKE algorithm support
An HPKE variant relies on three types of algorithms: KEM, KDF, and AEAD. Table describes the HPKE algorithms that KMB supports.
Table: HPKE algorithm support {#hpke-algorithm-support}
| Algorithm type | Algorithm | IANA code point | Reference document |
|---|---|---|---|
| KEM +---------------------+-----------+ for HPKE | ML-KEM-1024 ML-KEM-1024 + P-384 | 0x0042 0x0052 | PQ and PQ/T Hybrid Algorithms Post-Quantum and Post-Quantum/Traditional Hybrid Algorithms for HPKE |
| +---------------------+-----------+-------------------------------+ || P-384 | 0x0011 | RFC 9180 Hybrid Public Key Encryption |
+-----------+---------------------+-----------+ | | KDF | HKDF-SHA384 | 0x0002 ||
+-----------+---------------------+-----------+ | | AEAD | AES-256-GCM | 0x0002 ||
+-----------+---------------------+-----------+-------------------------------+
The definitions for the post-quantum KEMs are currently distributed across a number of documents. Hybrid PQ/T Key Encapsulation Mechanisms contains generic constructions, Concrete Hybrid PQ/T Key Encapsulation Mechanisms contains concrete parameterizations, and Post-Quantum and Post-Quantum/Traditional Hybrid Algorithms for HPKE contains HPKE IANA code-point assignments.3
As of this specification's publication, post-quantum HPKE KEMs are not fully ratified in IETF. However, the draft authors are confident that the IANA code points and associated definitions will not change before the drafts are finalized.
Figures [-Figure], [-Figure], and [-Figure] illustrate each KEM algorithm. The details of how the AES-GCM key and IV are derived are illustrated in Figure.
{#hpke-kem-mlkem}
{#hpke-kem-hybrid}
{#hpke-kem-ecdh}
{#hpke-key-derivation}
The HPKE sequence number is incremented for each decryption operation performed with keys derived from a given HPKE context. With the exception of MPK access key rotation as described in Section, all flows that accept HPKE payloads will perform a single decryption operation with the computed context. MPK access key rotation will perform two decryption operations and will increment the sequence number between them.
HPKE public key endorsement
Upon request, KMB can issue an endorsement for a given HPKE public key, signed by Caliptra runtime firmware's DICE alias key. The endorsement and its fields are populated by KMB firmware. This is illustrated in Figure.
{#hpke-pub-key-endorsement}
The endorsement includes the HPKEIdentifiers extension as defined in TCG Storage Feature Set: Media Encryption Key Multiparty Authorization, with the `kem_id`, `kdf_id`, and `aead_id` identifiers populated based on the HPKE keypair's algorithms. See Table for the algorithm identifiers supported by KMB.
MPK lifecycle
MPKs can be generated, enabled, and have their access keys rotated and tested.
MPK generation
Drive firmware may request that KMB generate an MPK, bound to a given access key.
To identify an MPK, at creation time drive firmware can provide "metadata" for the MPK, which is included in the generated Locked MPK and bound to the ciphertext as AAD. See Section for guidance on how drive firmware should populate the metadata field for the MPK. Note that "metadata" in this context refers to metadata about the MPK, and bears no relation to metadata about an MEK.
KMB performs the following steps:
- Unwrap the given MPK access key using the HPKE keypair held within KMB.
- Randomly generate an MPK.
- Derive an MPK encryption key from the HEK, SEK, and decrypted access key.
- Encrypt the MPK to the MPK encryption key, attaching the given metadata as AAD.
- Return the encrypted MPK to drive firmware.
Drive firmware may then store the encrypted MPK in persistent storage.
{#mpk-generation}
MPK enabling
Encrypted MPKs stored at rest in persistent storage are considered "locked", and must be enabled before they can be used to load an MEK. Enabled MPKs are encrypted to the VEK when handled by drive firmware. Enabled MPKs are invalidated when the VEK is rotated, which occurs on Caliptra cold reset. See Section.
To enable an MPK, KMB performs the following steps:
- Unwrap the given MPK access key using the HPKE keypair held within KMB.
- Derive the MPK encryption key from the HEK, SEK, and decrypted access key.
- Decrypt the MPK using the MPK encryption key.
- Encrypt the MPK using the VEK, preserving the MPK metadata.
- Return the re-encrypted "enabled" MPK to drive firmware.
Drive firmware may then stash the encrypted Enabled MPK in volatile storage, and later provide it to KMB when loading an MEK, as described in Section.
{#mpk-enabling}
MPK access key rotation
The access key to which an MPK is bound may be rotated. The user must prove that they have knowledge of both the current and new access key before a rotation is allowed. This is accomplished by the user wrapping both their current and new access key in the same HPKE payload. KMB performs the following steps:
- Unwrap the given current access key and new access key using the HPKE keypair held within KMB, incrementing the HPKE sequence number between each decryption operation. Note that the sequence number increment is a side-effect of the `ContextR.Open()` HPKE operation.
- Derive the current MPK encryption key from the HEK, SEK, and decrypted current access key.
- Derive the new MPK encryption key from the HEK, SEK, and decrypted new access key.
- Decrypt the MPK using the current MPK encryption key.
- Encrypt the MPK using the new MPK encryption key, preserving the MPK metadata.
- Return the re-encrypted MPK to drive firmware.
Drive firmware then zeroizes the old encrypted MPK and stores the new encrypted MPK in persistent storage.
{#mpk-access-key-rotation}
MPK access key testing
A user that has bound an access key to an MPK may wish to confirm that their access key is in fact bound to the MPK. This can be useful after an access key rotation, to ensure that the rotation executed successfully on the storage device.
To test an MPK, KMB receives a wrapped MPK, wrapped MPK access key, and freshness nonce. KMB then performs the following steps:
- Unwrap the given MPK access key using the HPKE keypair held within KMB.
- Derive the MPK encryption key from the HEK, SEK, and decrypted access key.
- Verify the integrity of the MPK ciphertext and AAD using the MPK encryption key, discarding the decrypted MPK in the process.
- Calculate and return the digest SHA2-384(MPK metadata || access key || nonce).
The user can then verify that the returned digest matches their expectation.
{#mpk-access-key-testing}
DPKs
Drive firmware provides a Data Protection Key (DPK) to KMB when generating, loading, or deriving an MEK. The DPK is used in a KDF, along with the HEK, SEK, and MPKs, to derive the MEK secret, as illustrated in Section.
Example flow when generating or loading an MEK
In this flow, the DPK may be encrypted by a user's C_PIN. Drive firmware logic which decrypts MEKs can be repurposed to produce the DPK. This flow may be used to support TCG Opal or Enterprise.
{#dpk-decryption}
Example flow when deriving an MEK
In this flow, the DPK may be the imported key associated with a Key Per I/O key tag.
{#dpk-injection}
HEKs and SEKs
KMB supports a pair of epoch keys: the Hard Epoch Key (HEK) and Soft Epoch Key (SEK). Both must be available, i.e. non-zeroized, in order for MEKs to be loaded.
- The HEK is derived from seeds held in Caliptra's fuse block, and is never visible outside of Caliptra. If the HEK is zeroized, KMB does not allow MEKs to be loaded, with edge cases detailed in Section.
- The SEK is managed by drive firmware, and may be stored in flash. If the SEK is not randomized, drive firmware is responsible for enforcing that MEKs are not loaded.
Zeroizing either the HEK or SEK is equivalent to performing a cryptographic erase. HEK zeroization is effectively a "hard" cryptographic erase, as it is highly difficult to recover secrets from a zeroized fuse bank.
The drive must only provide HEK seeds or SEKs to KMB that are cryptographically-strong random values, because otherwise it is not possible to cryptographically erase data through zeroization of those values. Note: as depicted in Figure, MCU ROM may provide an all-zeroes HEK seed to KMB, provided that it correctly indicates to KMB that the HEK seed is not randomized.
HEK lifecycle
Caliptra with L.O.C.K. features a series of n 256-bit HEK slots present in a fuse bank, dubbed HEK~0~..HEK~n-1~, where 4 β€ n β€ 16. The vendor is responsible for determining the number of HEK slots available in fuses. These HEK slots are programmed and read by MCU, via the Caliptra fuse controller. MCU is responsible for transitioning each HEK slot individually from blank β randomized β zeroized. HEK~x~ shall only be randomized once HEK~x-1~ has been zeroized. MCU shall zeroize a HEK slot by blowing every bit.
Each HEK slot contains a HEK seed. MCU ROM reads the currently-randomized HEK slot from fuses during cold reset, and passes its contents to Caliptra Core via a fuse register.
There are two edge cases where the HEK is available even if there is no randomized seed available in the HEK fuse bank. In both cases, the HEK is derived from an all-zeroes HEK seed.
-
To enable pre-production testing, if Caliptra is in the Unprovisioned or Manufacturing lifecycle state, the HEK is available.
-
To enable lengthening the useful lifespan of the storage device, once all HEK slots have been zeroized, the device can be permanently transitioned into a mode where the HEK is available. A field-programmable "permanent-HEK" logical fuse bit is allocated to determine whether this mode is active.
Table describes the states between which the HEK transitions over the lifespan of the device. The lifecycle state of the HEK is determined by Caliptra's lifecycle state as well as the state of the HEK fuse bank.
Table: HEK lifecycle states {#hek-lifecycle-states}
| Caliptra lifecycle state | HEK fuse state, where x is the current HEK slot | HEK seed value | Reported HEK state |
|---|---|---|---|
| Unprovisioned or Manufacturing | [Any] | 0x0000... | HEK_AVAIL_UNERASABLE |
| Production | HEK slot 0 is unprogrammed. | N/A | HEK_UNAVAIL_EMPTY |
| Production | HEK slot x is randomized. | HEK slot x | HEK_AVAIL_PROGRAMMED |
| Production | HEK slot x is zeroized. | N/A | HEK_UNAVAIL_ZEROIZED |
| Production | HEK slot x is corrupted. | N/A | HEK_UNAVAIL_CORRUPTED |
| Production | Every HEK slot is zeroized, and the permanent-HEK fuse bit has been set. | 0x0000... | HEK_AVAIL_UNERASABLE |
In addition to the HEK seed, the HEK is derived from Caliptra's DICE UDS. The mechanism used to compute the HEK is described in Figure. This flow is performed across MCU ROM and Caliptra ROM upon KMB startup.
{#hek-derivation}
MCU ROM is responsible for invoking the REPORT_HEK_METADATA command exactly once, after cold reset and before Caliptra FMC/runtime firmware is first loaded. If REPORT_HEK_METADATA is not invoked, KMB will zeroize the HEK and runtime functions which rely on it will be disabled.
By deriving the HEK in ROM based on secrets that are not available to runtime firmware, KMB ensures that compromised runtime firmware cannot derive the HEK using replayed HEK seed values.
Note: the HEK is derived before REPORT_HEK_METADATA is invoked due to IDevID having been zeroized from the Key Vault by the time Caliptra ROM is ready to handle mailbox commands.
HEK and SEK lifecycle
Figure illustrates the case where a drive ships with four HEK slots.
{#hek-sek-rotation}
Drive firmware is responsible for performing each state transition, including programming and zeroizing HEK seeds and entering perma-HEK mode.
A device out of manufacturing must be in the production lifecycle state and have a randomized HEK.
Drive firmware is responsible for enforcing that HEK and SEK programming / zeroization follows the state machine illustrated in Figure. Specifically:
- The SEK shall only be programmed once the HEK is available.
- The HEK seed shall only be zeroized once the SEK is zeroized.
- The HEK seed shall only be zeroized after it has been randomized or corrupted.
- The next HEK seed shall only be programmed once the prior HEK seed has been zeroized.
- Perma-HEK mode shall only be enabled once all HEK seeds have been zeroized.
Drive firmware is responsible for zeroizing the encryption engine's key cache when the SEK transitions from randomized to zeroized. HEK transitions involve power cycles and therefore the key cache is implicitly zeroized across such transitions.
MEKs
KMB can encrypt randomly-generated MEKs, or compute derived MEKs.
Figure provides details on how the SEK, HEK, DPK, and MPKs are used to produce the MEK secret, which then either encrypts/decrypts a random MEK or is used to compute a derived MEK.
{#mek-secret-derivation}
Note: it is drive firmware's responsibility to ensure that MPKs are mixed in the correct order for a given MEK.
See Section for how the MEK secret is used to generate and load random MEKs. See Section for how the MEK secret is used to derive deterministic MEKs.
Support for non-MPA storage API flows
KMB allows an MEK to be bound to zero MPKs, which supports TCG flows that do not interact with the TCG MEK-MPA specification. The process for establishing such an MEK is illustrated in Figure.
{#mek-secret-derivation-no-mpks}
Protecting MEKs with the MDK
Randomly-generated MEKs are encrypted at rest with AES-GCM, as illustrated in Section. During initial MEK encryption, Caliptra's AES engine accepts the MEK plaintext from KMB firmware and returns the ciphertext back to KMB firmware. Later, during MEK decryption, Caliptra's AES engine accepts the MEK ciphertext from KMB firmware and writes the decrypted plaintext to the Key Vault. From Caliptra hardware's perspective, AES-GCM encryption and decryption are the same operation. Therefore, if the MEK were protected with only a single layer of AES-GCM encryption, compromised KMB firmware could instruct Caliptra's AES engine to treat decryption like encryption and return the decrypted plaintext to KMB firmware, trivially disclosing the MEK.
To mitigate this, Caliptra hardware enforces that before an MEK can be loaded into the Key Vault, it must first pass through an AES-ECB decryption operation keyed using the MEK Deobfuscation Key (MDK). The MDK is derived in ROM upon cold reset, as described in Section and illustrated in Figure.
Unlike with AES-GCM, Caliptra's AES engine can differentiate between encryption and decryption in AES-ECB mode. Hardware enforces that for any AES-ECB decryption operation using the Key Vault slot that holds the MDK, the result is always routed to the Key Vault. The MDK encryption layer therefore ensures that the plaintext MEK is never visible to KMB firmware once the MEK has been decrypted.
By deriving the MDK in ROM based on secrets that are not available to runtime firmware, KMB ensures that compromised runtime firmware cannot re-derive the MDK into a separate Key Vault slot, which could have looser protections for whether firmware can see the results of an AES-ECB decryption operation.
{#mdk-derivation}
Generating and loading a random MEK
Figures [-Figure] and [-Figure] elide the process of deriving the MEK secret, as those details are captured in Figure. When an MEK is generated or loaded, the inputs given in Figure are provided by drive firmware so that KMB may derive the MEK secret.
Randomly-generated MEKs are given two layers of at-rest encryption: an inner AES-ECB layer using the MDK, and an outer AES-GCM layer using the MEK secret. The wrapped MEK's integrity tag is associated with the outer layer. The inner AES-ECB layer does not have its own MAC. Therefore the extra layer of encryption does not modify the size of the wrapped MEK. See Section for details on the format and layout of the wrapped MEK.
{#mek-generate}
{#mek-load}
Deriving an MEK
Figure elides the process of deriving the MEK secret, as those details are captured in Figure. When an MEK is derived, the inputs given in Figure are provided by drive firmware so that KMB may derive the MEK secret.
{#mek-derive}
Deobfuscating MEK seeds with the MDK
The MEK seed is calculated via CMAC instead of HMAC, and then "decrypted" via the MDK, for the following reasons:
- As described in Section, the only way to populate the Key Vault slot that holds the MEK is via the AES engine operating in ECB-decrypt mode (where the key is taken from the Key Vault slot that holds the MDK).
- The AES engine does not allow messages to be read from the Key Vault. Therefore KMB must derive a key from the MEK secret that is readable by firmware, so firmware can pass it as an AES message to compute the MEK.
- Since the MEK secret is in the Key Vault, the result of any HMAC operations would be required to go back to the Key Vault, and not be available to firmware.
- The CMAC operation, by contrast, utilizes the AES engine in a manner that allows the results to be readable by firmware. Therefore CMAC is used to compute the MEK seed from the MEK secret.
- The resulting MEK seed is then passed as an AES message to the AES engine, which uses it in a decryption operation to produce the derived MEK.
Derived MEK checksums
When deriving an MEK, drive firmware provides an MEK checksum to KMB. See Figure for details on how the checksum is derived. If the provided checksum is non-zero, it is compared with the calculated checksum. The operation fails (and the derived MEK is not sent to the encryption engine) if the provided checksum does not match the calculated checksum. This check is skipped if the provided checksum is all-zeroes.
Upon success, the calculated checksum is returned to drive firmware. Drive firmware may choose to store the checksum persistently and use it for future derivations, or may discard the returned checksum. The checksum allows drive firmware to ensure that an MEK derived in the future matches a given MEK derived previously. This check would be a precaution against mistakes or bit flips that would result in an incorrect MEK being programmed to the encryption engine, which could lead to data loss. This specification does not define how drive firmware should react to a checksum mismatch.
The checksum is not derived from the MEK directly, because Caliptra hardware restricts what operations can be done with the Key Vault slot that holds the MEK.
An example flow where the MEK checksum would be useful is as follows:
- Drive firmware is instructed to establish a new MEK.
- Drive firmware instructs KMB to produce the new MEK via DERIVE_MEK, as part of the sequence specified in Section.
- Upon success, drive firmware is given the derived-MEK's checksum, which it stores in non-volatile memory.
- On a subsequent boot, drive firmware reads the SEK into volatile memory, and is later asked to re-establish the MEK it derived in step 2.
- The copy of the SEK held in volatile memory has suffered corruption since it was last read from persistent storage, and a bit has flipped.
- Drive firmware invokes DERIVE_MEK. As part of this command, drive firmware provides the derived-MEK checksum it saved in non-volatile storage.
- KMB detects the checksum mismatch and refuses to program the derived MEK. Drive firmware surfaces this failure to the host.
In this example, if the user had proceeded to write data to the disk using the MEK derived in step 6, that data would not be recoverable on subsequent boots unless the SEK experienced the exact same bit flip pattern as in step 5. The derived-MEK checksum allows the drive to detect this scenario and alert the user. Note that there are other possible mechanisms for mitigating this risk. The use of derived-MEK checksums is optional.
Note: a checksum is not produced for wrapped MEKs. For wrapped MEKs, any change in the inputs which produce the MEK secret would result in an error during AES-GCM decryption of the provided MEK.
MEK command sequence
KMB exposes the following mailbox commands for establishing an MEK:
Figure illustrates how these commands are used in sequence.
{#mek-command-sequence}
Integrators may elect to utilize either MEK generation or MEK derivation when establishing an MEK.
AES-XTS considerations
The MEK sent to the encryption engine may be used as an AES-XTS key. FIPS 140-3 IG Implementation Guidance for FIPS 140-3 and the Cryptographic Module Validation Program section C.I states that in AES-XTS, the key is "parsed as the concatenation of two AES keys, denoted by Key_1 and Key_2, that are 128 [or 256] bits long... Key_1 and Key_2 shall be generated and/or established independently according to the rules for component symmetric keys from NIST SP 800-133rev2, Sec. 6.3. The module shall check explicitly that Key_1 β Key_2, regardless of how Key_1 and Key_2 are obtained."
SP 800-133 Recommendation for Cryptographic Key Generation (Rev. 2) section 6.3 states: "The independent generation/establishment of the component keys K~1~, ..., K~n~ is interpreted in a computational and a statistical sense; that is, the computation of any particular K~i~ value does not depend on any one or more of the other K~i~ values, and it is not feasible to use knowledge of any proper subset of the K~i~ values to obtain any information about the remaining K~i~ values."
SP 800-108 Recommendation for Key Derivation Using Pseudorandom Functions (Rev. 1) section 4 states that "the output of a key-derivation function is called the derived keying material and may subsequently be segmented into multiple keys. Any disjoint segments of the derived keying material (with the required lengths) can be used as cryptographic keys for the intended algorithms."
As the MEK sent to the encryption engine is either randomly or pseudorandomly generated, it satisfies the above constraints. Disjoint segments of derived keying material computed from a pseudorandom function are statistically and computationally independent.
When in AES-XTS mode, the encryption engine will be responsible for performing the inequality check for Key_1 and Key_2 stipulated by FIPS 140-3 IG section C.I. If this check fails, the encryption engine must report a vendor-defined error. This document does not specify how drive firmware should handle this error case.
Random key generation via DRBG
KMB generates multiple kinds of random keys. It does so using randomness obtained from a DRBG, which is seeded from a TRNG and which may be updated with entropy from the host.
Caliptra hardware Caliptra 2.0 Hardware Specification supports either an integrated or external DRBG. Caliptra Subsystem requires leveraging the integrated DRBG. This construction is illustrated in Figure.
{#drbg-internal}
Host-provided entropy can be mixed into the DRBG state via the CM_RANDOM_STIR Caliptra mailbox command.
Drive firmware can obtain randomness from Caliptra's DRBG via the CM_RANDOM_GENERATE Caliptra mailbox command.
Authorization
KMB exposes commands that allows drive firmware to manage the lifecycle of MPKs, HEKs, and MEKs. These lifecycle events have the potential to (and are in many cases designed to) trigger user data loss. The host interface that allows the host to trigger these lifecycle events must therefore implement authorization controls to ensure user data is not improperly destroyed. Drive firmware is responsible for implementing this authorization layer, the details of which are outside the scope of this specification.
Reset behavior
This section defines expected behavior for KMB across each Caliptra reset type, as defined in Caliptra 2.0 Integration Specification.
Table: Behavior on Caliptra reset types {#caliptra-reset-types}
| Caliptra reset type | Behavior |
|---|---|
| Cold boot / cold reset | Caliptra ROM derives the HEK and MDK, then sets the LOCK_IN_PROGRESS bit in the SS_OCP_LOCK_CTRL register. Next, Caliptra runtime firmware performs the following steps: - Determines whether a HEK is available, based on the fuse configuration. If a HEK is unavailable, KMB enters a mode where MEKs are not allowed to be loaded. - Initializes random HPKE keypairs for each supported algorithm, and assigns handles to them, reported via ENUMERATE_HPKE_HANDLES. The VEK is not initialized upon cold reset, as it is generated lazily upon first use. See Section for details. |
| Warm reset / firmware update reset | The HEK, MDK, and VEK are undisturbed. HPKE keypairs are discarded prior to reset and are regenerated after reset. |
Caliptra cold boot / cold reset shall only be triggered by an NVMe subsystem reset NVM Express Base Specification, Revision 2.2 caused by main power being applied to the subsystem.
A given integration may trigger a Caliptra warm reset in response to other reset events.
Interfaces
OCP L.O.C.K. defines two interfaces:
- The encryption engine interface is exposed from the vendor-implemented encryption engine to KMB, and defines a standard mechanism for programming MEKs and control messages.
- The mailbox interface is exposed from KMB to storage drive firmware, and enables the drive to manage MEKs and associated keys.
Note: as of this specification's publication, KMB firmware has not yet been implemented. As such, certain details of the interface specification are subject to change. This notice will be removed once firmware has been implemented.
Encryption engine interface
This section defines the interface between KMB and an encryption engine. An encryption engine is used to encrypt/decrypt user data. Its design and implementation are vendor specific. MEKs are generated or derived within KMB and used by the encryption engine to encrypt and decrypt user data. The interface defined in this section is used to load MEKs from KMB to the encryption engine or to cause the encryption engine to unload (i.e., remove) loaded MEKs. MEKs shall not be accessible to drive firmware as they are being transferred between KMB and the encryption engine.
Overview
The encryption engine uses an MEK stored in volatile memory to encrypt and decrypt user data. For the purposes of this specification, the entity within the encryption engine used to store the MEKs is called the key cache. Each encryption and decryption of user data is coupled to a specific MEK which is stored in the key cache bound to a unique identifier, called metadata. Each (metadata, MEK) pair is also associated with additional information, called aux, which is used neither as MEK nor an identifier, but has some additional information about the pair. Therefore, the key cache as an entity which stores (metadata, aux, MEK) tuples.
To ensure that MEKs are only ever visible to KMB and the encryption engine, KMB is the only entity which can load and unload (metadata, aux, MEK) tuples. Drive firmware arbitrates all operations in the KMB β encryption engine interface, and is therefore responsible for managing which MEK is loaded in the key cache. Drive firmware has full control on metadata and optional aux. Figure is an illustration of the KMB β encryption engine interface which shows:
- The tuple for loading an MEK.
- The metadata for unloading an MEK.
- An example of a key cache configuration within the encryption engine.
{#encryption-engine}
The behavior of I/O through the encryption engine during the execution of an SFR command is vendor-defined.
Special Function Registers
KMB uses Special Function Registers (SFRs) to communicate with the encryption engine as shown in Table and each of the following subsections which describe the registers.
The OCP_LOCK_MEK_ADDRESS register represents the combined registers SS_KEY_RELEASE_BASE_ADDR_L and SS_KEY_RELEASE_BASE_ADDR_H and shall contain the fixed base address of the MEK register.
The SS_KEY_RELEASE_SIZE register shall contain the size of the MEK, in bytes. This shall be set to 40h by the MCU ROM.
MCU ROM is responsible for configuring SS_KEY_RELEASE_BASE_ADDR_L, SS_KEY_RELEASE_BASE_ADDR_H and SS_KEY_RELEASE_SIZE, prior to setting CPTRA_FUSE_WR_DONE to prevent further modifications.
Table: KMB to encryption engine SFRs {#kmb-ee-sfrs}
| Register | Address | Byte Size | Description |
|---|---|---|---|
| Media Encryption Key (MEK) | OCP_LOCK_MEK_ADDRESS | 40h | Register to provide MEK. |
| Metadata (METD) | OCP_LOCK_MEK_ADDRESS + 40h | 14h | Register to provide metadata |
| Auxiliary Data (AUX) | OCP_LOCK_MEK_ADDRESS + 60h | 20h | Register to provide auxiliary values |
| Control | OCP_LOCK_MEK_ADDRESS + 80h | 4h | Register to handle commands |
OCP_LOCK_MEK_ADDRESS contains the base address for the SFRs shown in Table. The vendor is responsible for ensuring that KMB can access these SFRs through these addresses.
For alignment, offsets OCP_LOCK_MEK_ADDRESS + OCP_LOCK_MEK_LENGTH + 14h..20h (inclusive) are intentionally unallocated.
In the register definitions that follow, the "Type" column indicates whether KMB may read from or write to the given register.
Media Encryption Key register
Table: OCP_LOCK_MEK_ADDRESS: MEK β Media Encryption Key {#ee-mek-sfr}
| Bytes | Type | Reset | Description |
|---|---|---|---|
| 63:00 | WO | 0h | Media encryption key: This field specifies a 512-bit encryption key. |
The encryption engine is free to interpret the provided key in a vendor-defined manner. One sample interpretation for AES-XTS-256 is presented in Figure.
{#mek-format-example-for-aes}
If an algorithm used by the encryption engine does not require 512 bits of key material, the encryption engine is free to disregard unused bits.
Within KMB, loaded MEKs are only ever present in the Key Vault, so that they can be protected against firmware-level attacks. KMB will write MEKs into the encryption engine's key cache using the DMA engine. The DMA engine will copy the key value stored in Key Vault slot 23 to the destination address, specified by OCP_LOCK_MEK_ADDRESS.
Metadata register
Table defines the Metadata register used to pass additional data related to the MEK.
Table: Offset OCP_LOCK_MEK_ADDRESS + 40h: METD β Metadata {#ee-metd-sfr}
| Bytes | Type | Reset | Description |
|---|---|---|---|
| 19:00 | RW | 0h | Metadata (METD): This field specifies metadata that is vendor specific and specifies the entry in the encryption engine for the MEK. |
KMB and the encryption engine must be the only components which have access to MEKs. Each MEK is associated with non-secret metadata, in order for the MEK to be used for any key-related operations including data I/O. The METD field is used to convey this metadata.
When loading or deriving an MEK, KMB takes an METD value as input from drive firmware and writes it to the METD register without any modification. This allows the vendor full control of the MEK indexing algorithm.
Two examples of how a storage device might leverage the METD field are provided: Logical Block Addressing (LBA) range-based metadata, and key-tag based metadata.
When an SSD stores data with address-based encryption, an MEK can be uniquely identified by an (LBA range, Namespace ID) pair. Then, the (LBA range, Namespace ID) pair can be leveraged into METD as illustrated in Figure.
{#lba-nsid-layout}
Address-based encryption is not the only encryption mechanism in SSDs. For example, in TCG Key Per I/O, an MEK is selected by a key tag, which does not map to an address. Figure shows an example of METD in such cases.
{#key-tag-layout}
The above examples are not the only possible values of METD. Vendors may design and use their own METD if it is more suitable for their system.
Auxiliary Data register
Table defines the Auxiliary Data register used to pass additional vendor-specific data related to the MEK.
Table: OCP_LOCK_MEK_ADDRESS + 60h: AUX β Auxiliary Data {#ee-aux-sfr}
| Bytes | Type | Reset | Description |
|---|---|---|---|
| 31:00 | RW | 0h | Auxiliary Data (AUX): This field specifies auxiliary data associated to the MEK. |
The AUX field supports vendor-specific features on MEKs. KMB itself only supports fundamental functionalities in order to minimize attack surfaces on MEKs. Moreover, vendors are free to design and implement their own MEK-related functionality within the encryption engine, as long as that functionality cannot be used to exfiltrate MEKs. In order to support these functionalities, some data may be associated and stored with an MEK, and the AUX field facilitates this association.
When drive firmware instructs KMB to load an MEK, drive firmware is expected to provide an AUX value. Similar to the METD field, KMB will write the AUX value into the Auxiliary Data register without any modification.
One simple use case of the AUX field is to store an offset of initialization vector or nonce. It can also be used in a more complicated use case. Here is an example. Suppose that there exists a vendor who wants to design a system which supports several modes of operation through the encryption engine while using KMB. Then, a structure of AUX value as on Figure can be used.
{#aux-register-format-example}
When drive firmware instructs KMB to load an MEK, drive firmware can use the AUX value to specify which mode of operation should be used and which value should be used as an initialization vector or a nonce with the generated MEK.
Control register
Table defines the Control register used to sequence the execution of a command and obtain the status of that command.
Table: Offset OCP_LOCK_MEK_ADDRESS + 80h: CTRL β Control {#ee-ctrl-sfr}
| Bits | Type | Reset | Description |
|---|---|---|---|
| 31 | RO | 0h | Ready (RDY): After any reset, the default value of this bit shall be 0b. Once the encryption engine is initialized and becomes ready to handle commands, the encryption engine shall set this bit from 0b to 1b. When a fatal error has occurred in the encryption engine, the encryption engine may set this bit from 1b to 0b. KMB checks that this bit is set to 1b before issuing any commands to the encryption engine. |
| 30:20 | RO | 0h | Reserved |
| 19:16 | RO | 0h | Error (ERR): When the encryption engine sets the DONE bit from 0b to 1b, the encryption engine sets this field to 0000b if the command specified by the CMD field succeeded, or a non-zero value if the command failed. See Table. Encryption engine error codes are surfaced back to drive firmware. If the DONE bit is set to 1b by KMB, then this field is set to 0000b. |
| 15:6 | RO | 0h | Reserved |
| 5:2 | RW | 0h | Command (CMD): This field specifies the command to execute or the command associated with the reported status. See Table. |
| 1 | RW | 0b | Done (DONE): This bit indicates the completion of a command by the encryption engine. If this bit is set to 1b by the encryption engine, then the encryption engine has completed the command specified by the CMD field and the ERR field indicates the status of the execution of that command. A write by KMB of the value 1b to this bit shall cause the encryption engine to: - set the DONE bit to 0b, - set the EXE bit to 0b, - set the ERR field to 0000b, and - set the CMD bit to 0000b. |
| 0 | RW | 0b | Execute (EXE): After any reset, the default value of this field shall be 0b. When this bit is set from 0b to 1b by KMB, the encryption engine shall execute the command specified in the CMD field. Once the execution is complete, the encryption engine shall simultaneously set this bit from 1b to 0b, set the DONE bit from 0b to 1b, and populate the ERR field. While the EXE bit is 1b, the encryption engine is busy. |
Table: CTRL error codes {#ee-ctrl-err-codes}
| Value | Description |
|---|---|
| 0h | Command successful |
| 1h | Invalid command code |
| 2h to 3h | Reserved |
| 4h to Fh | Vendor Specific |
Table: CTRL command codes {#ee-ctrl-cmd-codes}
| Value | Description |
|---|---|
| 0h | Reserved |
| 1h | Load MEK: Load the key specified by the AUX field and MEK register into the encryption engine as specified by the METD field. |
| 2h | Unload MEK: Unload the MEK from the encryption engine as specified by the METD field. |
| 3h | Zeroize: Unload all of the MEKs from the encryption engine (i.e., zeroize the encryption engine MEKs). |
| 4h to Fh | Reserved |
From KMB, the Control register is the register to write a command and receive its execution result. From its counterpart, the encryption engine, the Control register is used to receive a command and write its execution result.
The expected change flow of the Control register to handle a command is as follows:
- If RDY is set to 1b, then KMB writes CMD and EXE
- CMD: either 1h, 2h or 3h
- EXE: 1b
- The encryption engine writes ERR and DONE
- ERR: either 0b or a non-zero value depending on the execution result
- DONE: 1b
- KMB writes DONE
- DONE: 1b
- The encryption engine writes CMD, ERR, DONE and EXE
- CMD: 0h
- ERR: 0h
- DONE: 0b
- EXE: 0b
KMB therefore interacts with the Control register as follows in the normal circumstance:
- KMB writes CMD and EXE
- CMD: either 1h, 2h or 3h
- EXE: 1b
- KMB waits for DONE to be 1
- KMB writes DONE
- DONE: 1b
- KMB waits for DONE to be 0
Since the Control register is a part of the encryption engine whose implementation can be unique to each vendor, behaviors of the Control register with respect to unexpected flows are left for vendors. For example, a vendor who wants robustness might integrate a write-lock into the Control register in order to prevent two almost simultaneous writes on the EXE bit. Vendors shall ensure that any customizations remain compatible with the interface defined in this specification.
Control register state machine
Figure illustrates the control register's state machine as it transitions from power-on to executing a command.
{#ctrl-reg-state-machine}
KMB command sequence
Figure shows a sample command execution. This is an expected sequence when drive firmware instructs KMB to load an MEK. This sequence would occur as part of the DERIVE_MEK or LOAD_MEK mailbox commands. The internal behavior of the encryption engine is one of several possible mechanisms, and can be different per vendor.
PlantUML rendering error: PlantUML did not generate an image, did you forget the @startuml, @enduml block (java -jar ./plantuml-asl-1.2025.0.jar -tsvg -nometadata /tmp/.tmpjvNI2V/7028d09e983102582e39f5c9d214261a378690fa.puml)?
Mailbox interface
This section provides the mailbox commands exposed by Caliptra as part of OCP L.O.C.K.
FIPS status indicator
Each mailbox command returns a `fips_status` field. This provides an indicator of whether KMB is operating in FIPS mode. Table provides the possible values for this field.
Note: all multi-byte fields (i.e. `u16` and `u32`) that are not byte arrays are interpreted as little endian.
Table: Values for the FIPS status field {#fips-status-values}
| Value | Description |
|---|---|
| 0h | FIPS mode enabled. |
| 1h to FFFFh | Reserved. |
Encryption engine timeout
Each mailbox command that causes a command to execute on the encryption engine includes a `cmd_timeout` value indicating the amount of time KMB firmware will wait until the command has completed. If this timeout is exceeded, KMB aborts the command and reports a `LOCK_ENGINE_TIMEOUT` result code.
For such commands, KMB firmware will return a `LOCK_EE_NOT_READY` error immediately if the encryption engine is not ready to execute a command.
Side-channel mitigations
Several mailbox commands invoke ECDH and/or ML-KEM Decaps. These operations involve deterministic data-dependent operations and are potentially susceptible to timing attacks and power analysis. To mitigate such attacks, Caliptra leverages masking in hardware, and introduces random jitter delays in firmware before handling mailbox commands.
REPORT_HEK_METADATA
This command is exposed by Caliptra ROM and allows MCU ROM to report metadata about the HEK. This command must be called exactly once after each cold reset and before loading Caliptra's FMC and runtime firmware.
By exposing this command in ROM, Caliptra de-privileges runtime compromise of drive firmware that might cause it to mis-report the HEK seed state, which could lead to incorrect device state attestations.
Command Code: 0x5248_4D54 ("RHMT")
Table: REPORT_HEK_METADATA input arguments {#report-hek-metadata-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| total_slots | u16 | Total number of HEK seed slots. |
| active_slot | u16 | Currently-active HEK seed slot. Zero-indexed. |
| seed_state | u16 | HEK seed state. See Table. |
| padding | u16 | Reserved. |
Table: HEK seed state values {#hek-seed-state-values}
| Value | Description |
|---|---|
| 0h | HEK_SEED_UNAVAIL_EMPTY |
| 1h | HEK_SEED_UNAVAIL_ZEROIZED |
| 2h | HEK_SEED_UNAVAIL_CORRUPTED |
| 3h | HEK_SEED_AVAIL_PROGRAMMED |
| 4h | HEK_SEED_AVAIL_UNERASABLE |
| 5h to FFFFh | Reserved |
MCU ROM shall adhere to Table when populating the HEK metadata.
Table: Conditions for setting seed_state and active_slot {#hek-metadata-conditions}
| Condition | Value of seed_state | Value of active_slot |
|---|---|---|
| All HEK seed slots are blank. | HEK_SEED_UNAVAIL_EMPTY | 0 |
| One or more HEK seed slots are zeroized, and either the next seed slot is blank, or there are no remaining seed slots. | HEK_SEED_UNAVAIL_ZEROIZED | The last slot that was zeroized. |
| A HEK seed slot has been corrupted by an interrupted write. | HEK_SEED_UNAVAIL_CORRUPTED | The slot that is corrupted. |
| A HEK seed slot has been programmed with randomness. | HEK_SEED_AVAIL_PROGRAMMED | The slot that is programmed with randomness. |
| All HEK seed slots have been zeroized and the perma-HEK mode bit has been programmed. | HEK_SEED_AVAIL_UNERASABLE | The last slot that was zeroized. |
Table: REPORT_HEK_METADATA output arguments {#report-hek-metadata-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| flags | u32 | Additional information for the caller. See Table. |
| reserved | u32[3] | Reserved. |
Table: REPORT_HEK_METADATA output flags {#report-hek-metadata-output-flags}
| Name | Offset | Description |
|---|---|---|
| HEK_AVAILABLE | Byte 0 bit 31 | When set indicates HEK is available. |
GET_STATUS
Allows drive firmware to determine if the encryption engine is ready to process commands as well as vendor-defined drive encryption engine status data.
Command Code: 0x4753_5441 ("GSTA")
Table: GET_STATUS input arguments {#get-status-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
Table: GET_STATUS output arguments {#get-status-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32[4] | Reserved. |
| ctrl_register | u32 | Value of the CTRL register from the SFR interface. |
GET_ALGORITHMS
Allows drive firmware to determine the types of algorithms supported by KMB for endorsement, KEM, MPK, and access key generation.
Command Code: 0x4741_4C47 ("GALG")
Table: GET_ALGORITHMS input arguments {#get-algorithms-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
Table: GET_ALGORITHMS output arguments {#get-algorithms-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32[4] | Reserved. |
| endorsement_algorithms | u32 | Identifies the supported endorsement algorithms: - Byte 0 bit 0: ecdsa_secp384r1_sha384 The Transport Layer Security (TLS) Protocol Version 1.3 - Byte 0 bit 1: ml-dsa-87 Internet X.509 Public Key Infrastructure: Algorithm Identifiers for ML-DSA |
| hpke_algorithms | u32 | Identifies the supported HPKE algorithms: {kem/aead/kdf}_id - Byte 0 bit 0: 0x0011, 0x0002, 0x0002 Hybrid Public Key Encryption - Byte 0 bit 1: 0x0042, 0x0002, 0x0002 Post-Quantum and Post-Quantum/Traditional Hybrid Algorithms for HPKE - Byte 0 bit 2: 0x0052, 0x0002, 0x0002 Post-Quantum and Post-Quantum/Traditional Hybrid Algorithms for HPKE See Table for definitions of each HPKE algorithm identifier. |
| access_key_sizes | u32 | Indicates the length of plaintext access keys: - Byte 0 bit 0: 256 bits |
Each of the `endorsement_algorithms`, `hpke_algorithms`, and `access_key_sizes` fields shall be reported as a non-zero value.
CLEAR_KEY_CACHE
This command unloads all MEKs in the encryption engine.
Command Code: 0x434C_4B43 ("CLKC")
Table: CLEAR_KEY_CACHE input arguments {#clear-key-cache-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| cmd_timeout | u32 | Timeout in ms for command to encryption engine to complete. |
Table: CLEAR_KEY_CACHE output arguments {#clear-key-cache-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
ENUMERATE_HPKE_HANDLES
This command returns a list of all currently-active HPKE handles for resources held by KMB.
Command Code: 0x4548_444C ("EHDL")
Table: ENUMERATE_HPKE_HANDLES input arguments {#enumerate-hpke-handles-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
Table: ENUMERATE_HPKE_HANDLES output arguments {#enumerate-hpke-handles-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
| hpke_handle_count | u32 | Number of HPKE handles (N). |
| hpke_handles | HpkeHandle[N] | List of (HPKE handle value, HPKE algorithm) tuples. |
Table: HpkeHandle contents {#hpke-handle-contents}
| Name | Type | Description |
|---|---|---|
| handle | u32 | Handle for HPKE keypair held in KMB memory. |
| hpke_algorithm | u32 | HPKE algorithm. Shall be a bit value indicated as supported in Table. |
ENDORSE_HPKE_PUB_KEY
This command generates a signed certificate for the specified HPKE public key using the specified endorsement algorithm.
Command Code: 0x4548_504B ("EHPK")
Table: ENDORSE_HPKE_PUB_KEY input arguments {#endorse-hpke-pub-key-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| hpke_handle | u32 | Handle for HPKE keypair held in KMB memory. |
| endorsement_algorithm | u32 | Endorsement algorithm identifier. If 0h, then just return public key. |
Table: ENDORSE_HPKE_PUB_KEY output arguments {#endorse-hpke-pub-key-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
| pub_key_len | u32 | Length of HPKE public key (`Npk` in RFC 9180). |
| endorsement_len | u32 | Length of endorsement data. Zero if `endorsement_algorithm` is 0h. |
| pub_key | u8[pub_key_len] | HPKE public key. |
| endorsement | u8[endorsement_len] | DER-encoded X.509 certificate. |
ROTATE_HPKE_KEY
This command rotates the HPKE keypair indicated by the specified handle and stores the new HPKE keypair in volatile memory within KMB.
Command Code: 0x5248_504B ("RHPK")
Table: ROTATE_HPKE_KEY input arguments {#rotate-hpke-key-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| hpke_handle | u32 | Handle for old HPKE keypair held in KMB memory. |
Table: ROTATE_HPKE_KEY output arguments {#rotate-hpke-key-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
| hpke_handle | u32 | Handle for new HPKE keypair held in KMB memory. |
GENERATE_MPK
This command unwraps the specified access key, generates a random MPK, then encrypts the MPK with a Locked MPK encryption key. The Locked MPK is returned for the drive to persistently store. The given `metadata` is placed in the `metadata` field of the returned MPK to cryptographically tie them together.
Command Code: 0x474D_504B ("GMPK")
Table: GENERATE_MPK input arguments {#generate-mpk-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| sek | u8[32] | Soft Epoch Key. |
| metadata_len | u32 | Length of the metadata argument. |
| metadata | u8[metadata_len] | Metadata for the MPK. |
| sealed_access_key | SealedAccessKey | HPKE-sealed access key. |
Table: GENERATE_MPK output arguments {#generate-mpk-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
| encrypted_mpk | LockedMpk | MPK encrypted to access_key. |
REWRAP_MPK
This command unwraps current_access_key and encrypted new_access_key from sealed_access_keys. Then current_access_key is used to decrypt new_access_key. The specified MPK is decrypted using its current Locked MPK encryption key, then re-encrypted with its new Locked MPK encryption key. The new Locked MPK is returned.
The drive stores the returned Locked MPK and zeroizes the old Locked MPK.
Command Code: 0x5245_5750 ("REWP")
Table: REWRAP_MPK input arguments {#rewrap-mpk-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| sek | u8[32] | Soft Epoch Key. |
| current_locked_mpk | LockedMpk | Current MPK to be rewrapped. |
| sealed_access_key | SealedAccessKey | HPKE-sealed current access key. |
| new_ak_ciphertext | u8[access_key_len+Nt] | New access key ciphertext and authentication tag. `access_key_len` is provided by the SealedAccessKey. `Nt` is the HPKE value associated with the `aead_id` identifier from the SealedAccessKey's `hpke_algorithm`. See Section for details. |
Table: REWRAP_MPK output arguments {#rewrap-mpk-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
| new_locked_mpk | LockedMpk | MPK encrypted to new_access_key. |
ENABLE_MPK
This command decrypts `sealed_access_key`, then uses it to derive a Locked MPK encryption key, which is used to decrypt `locked_mpk`. The MPK is then re-encrypted with the VEK. The Enabled MPK is returned.
Command Code: 0x524D_504B ("RMPK")
Table: ENABLE_MPK input arguments {#enable-mpk-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| sek | u8[32] | Soft Epoch Key. |
| sealed_access_key | SealedAccessKey | HPKE-sealed access key. |
| locked_mpk | LockedMpk | MPK encrypted to the HEK and access key. |
Table: ENABLE_MPK output arguments {#enable-mpk-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
| enabled_mpk | EnabledMpk | MPK encrypted to the VEK. |
INITIALIZE_MEK_SECRET
This command shall initialize the MEK secret seed as described in Figure.
Command Code: 0X494D_0B53 ("IMKS")
Table: INITIALIZE_MEK_SECRET input arguments {#initialize-mek-secret-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| sek | u8[32] | Soft Epoch Key. |
| dpk | u8[32] | Data Protection Key. |
Table: INITIALIZE_MEK_SECRET output arguments {#initialize-mek-secret-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
MIX_MPK
This command decrypts the specified MPK with the VEK, and then updates the MEK secret seed in KMB by performing a KDF with the MEK secret seed and the decrypted MPK.
When generating an MEK, one or more MIX_MPK commands are processed to modify the MEK secret seed.
The MEK secret seed must already be initialized by INITIALIZE_MEK_SECRET or this command shall return an error.
Command Code: 0x4D4D_504B ("MMPK")
Table: MIX_MPK input arguments {#mix-mpk-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| enabled_mpk | EnabledMpk | MPK encrypted to the VEK. |
Table: MIX_MPK output arguments {#mix-mpk-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
TEST_ACCESS_KEY
This command is used by the host to check the input access key is associated with the given MPK. The `nonce` is a random value to be included in the digest calculation to prevent response replay attacks. The output is calculated as SHA2-384(metadata || decrypted access key || nonce). The metadata is taken from the provided MPK's `metadata` field.
Command Code: 0x5441_434B ("TACK")
Table: TEST_ACCESS_KEY input arguments {#test-access-key-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| sek | u8[32] | Soft Epoch Key. |
| nonce | u8[32] | Host-provided random value. |
| locked_mpk | LockedMpk | Locked MPK associated with the access key. |
| sealed_access_key | SealedAccessKey | HPKE-sealed access key. |
Table: TEST_ACCESS_KEY output arguments {#test-access-key-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| digest | u8[48] | SHA-384 hash of the MPK's metadata, decrypted access key, and nonce. |
GENERATE_MEK
This command generates a random 512-bit MEK and encrypts it using the MEK secret.
INITIALIZE_MEK_SECRET must be invoked prior to this command or an error shall be returned.
Command Code: 0x474D_454B ("GMEK")
Table: GENERATE_MEK input arguments {#generate-mek-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
Table: GENERATE_MEK output arguments {#generate-mek-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
| wrapped_mek | WrappedMek | MEK encrypted to the derived MEK secret. |
LOAD_MEK
This command decrypts the given encrypted 512-bit MEK using the MEK secret.
The decrypted MEK, specified metadata, and aux_metadata are loaded into the encryption engine key cache. The metadata format is vendor-defined and specifies the information to the encryption engine on where within the key cache the MEK is loaded.
INITIALIZE_MEK_SECRET must be invoked prior to this command or an error shall be returned.
Command Code: 0x4C4D_454B ("LMEK")
Table: LOAD_MEK input arguments {#load-mek-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| metadata | u8[20] | Metadata for MEK to load into the drive encryption engine (i.e. NSID + LBA range). |
| aux_metadata | u8[32] | Auxiliary metadata for the MEK (optional; i.e. operation mode). |
| wrapped_mek | WrappedMek | MEK encrypted to the derived MEK secret. |
| cmd_timeout | u32 | Timeout in ms for command to encryption engine to complete. |
Table: LOAD_MEK output arguments {#load-mek-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
DERIVE_MEK
This command derives an MEK using the MEK secret.
The derived MEK, specified metadata, and aux_metadata are loaded into the encryption engine key cache. The metadata format is vendor-defined and specifies the information to the encryption engine on where within the key cache the MEK is loaded.
INITIALIZE_MEK_SECRET must be invoked prior to this command or an error shall be returned.
Command Code: 0x444D_454B ("DMEK")
Table: DERIVE_MEK input arguments {#derive-mek-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| mek_checksum | u8[16] | Checksum used to validate the derived MEK. May be all-zeroes, in which case no check is performed. |
| metadata | u8[20] | Metadata for MEK to load into the drive encryption engine (i.e. NSID + LBA range). |
| aux_metadata | u8[32] | Auxiliary metadata for the MEK (optional; i.e. operation mode). |
| cmd_timeout | u32 | Timeout in ms for command to encryption engine to complete. |
Table: DERIVE_MEK output arguments {#derive-mek-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
| mek_checksum | u8[16] | Checksum calculated for the derived MEK. |
UNLOAD_MEK
This command causes the MEK associated to the specified metadata to be unloaded for the key cache of the encryption engine. The metadata format is vendor-defined and specifies the information to the encryption engine on where within the key cache, the MEK is loaded.
Command Code: 0x554D_454B ("UMEK")
Table: UNLOAD_MEK input arguments {#unload-mek-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| metadata | u8[20] | Metadata for MEK to unload from the drive encryption engine (i.e. NSID + LBA range). |
| cmd_timeout | u32 | Timeout in ms for command to encryption engine to complete. |
Table: UNLOAD_MEK output arguments {#unload-mek-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
GET_EPOCH_KEY_STATE
This command reports the state of the epoch keys. The drive indicates the state of the SEK, while KMB internally senses the state of the HEK.
Command Code: 0x4745_4B53 ("GEKS")
Table: GET_EPOCH_KEY_STATE input arguments {#get-epoch-key-state-input-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. |
| reserved | u32 | Reserved. |
| sek_state | u16 | SEK state. See Table. |
| padding | u16 | Reserved. |
| nonce | u8[16] | Freshness nonce to be included in the signed IETF EAT. |
Table: GET_EPOCH_KEY_STATE output arguments {#get-epoch-key-state-output-args}
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| reserved | u32 | Reserved. |
| hek_erasures_remaining | u16 | Remaining number of times the HEK may be erased. See Section for how this field is calculated. |
| hek_state | u16 | State of the currently-active HEK. See Table. |
| sek_state | u16 | SEK state from the input argument. |
| eat_len | u16 | Total length of the IETF EAT. |
| nonce | u8[16] | Nonce from the input argument. |
| eat | u8[eat_len] | CBOR-encoded and signed IETF EAT. See Section for the format. |
Table: SEK state values {#sek-state-values}
| Value | Description |
|---|---|
| 0h | SEK_ZEROIZED |
| 1h | SEK_PROGRAMMED |
| 2h to FFFFh | Reserved |
The value reported from Table depends on Caliptra's current lifecycle state as well as the `seed_state` value from REPORT_HEK_METADATA.
Table: HEK state values {#hek-state-values}
| Value | State | Description |
|---|---|---|
| 0h | HEK_UNAVAIL_EMPTY | The drive has transitioned to the Production life cycle state and a HEK has not yet been provisioned. Reported if `seed_state` is HEK_SEED_UNAVAIL_EMPTY. |
| 1h | HEK_UNAVAIL_ZEROIZED | The current HEK has been zeroized. Reported if `seed_state` is HEK_SEED_UNAVAIL_ZEROIZED. |
| 2h | HEK_UNAVAIL_CORRUPTED | The current HEK slot is corrupted. Reported if `seed_state` is HEK_SEED_UNAVAIL_CORRUPTED. |
| 3h | HEK_AVAIL_PROGRAMMED | A randomized HEK is provisioned in the current slot. Reported if `seed_state` is HEK_SEED_AVAIL_PROGRAMMED. |
| 4h | HEK_AVAIL_UNERASABLE | The HEK is not used to provide sanitization capabilities. In this state, data written to the storage device cannot be cryptographically erased via HEK zeroization. Reported if Caliptra is not in the production lifecycle state or if `seed_state` is HEK_SEED_AVAIL_UNERASABLE. KMB internally derives a HEK in this state, but the HEK is derived from non-erasable secrets. |
| 5h to FFFFh | Reserved | Reserved. |
Operations that use the HEK are disallowed in all `HEK_UNAVAIL_*` states.
See Table for more information about the relationship between HEKs and Caliptra lifecycle states.
HEK_AVAIL_UNERASABLE β HEK_UNAVAIL_EMPTY is a one-way transition that the device vendor is expected to trigger by advancing the Caliptra lifecycle state to Production.
HEK_UNAVAIL_ZEROIZED β HEK_AVAIL_UNERASABLE is a one-way transition that the device owner can trigger once all HEK slots have been zeroized.
See Figure for more details about the transitions between HEK states.
Calculation of hek_erasures_remaining
The `hek_erasures_remaining` field in GET_EPOCH_KEY_STATE is based on the following values from REPORT_HEK_METADATA:
- total_slots
- active_slot
- seed_state
The calculation is as follows:
\[ \text{seed_is_erased}= \begin{cases} true & \text{if seed_state}=\text{HEK_SEED_UNAVAIL_ZEROIZED}\ true & \text{if seed_state}=\text{HEK_SEED_AVAIL_UNERASABLE}\ false & \text{otherwise} \end{cases} \] \[ \text{hek_erasures_remaining}=\text{total_slots}-\text{active_slot}- \begin{cases} 1 & \text{if seed_is_erased}\ 0 & \text{otherwise} \end{cases} \]
Common mailbox types
This section defines common types used to interface between drive firmware and KMB. These types are common patterns found in both requests and responses.
SealedAccessKey type
This type holds an HPKE-wrapped access key.
Table: SealedAccessKey contents {#sealed-access-key-contents}
| Name | Type | Description |
|---|---|---|
| hpke_handle | u32 | Handle for HPKE keypair held in KMB memory. |
| hpke_algorithm | u32 | HPKE algorithm. Must be a bit value indicated as supported in Table. |
| access_key_len | u32 | Access key length in bytes. Must be the scalar value associated with a bit value indicated as supported in Table. |
| info_len | u32 | Length of the info field. |
| info | u8[info_len] | Info to use with HPKE unwrap. |
| kem_ciphertext | u8[Nenc] | HPKE encapsulated key. |
| ak_ciphertext | u8[access_key_len+Nt] | Access key ciphertext and authentication tag. |
`Nenc` and `Nt` are HPKE values associated with the `kem_id` and `aead_id` identifiers from the given `hpke_algorithm`. For example, if byte 0 bit 0 of `hpke_algorithm` is set (indicating `kem_id` 0x0011 and `aead_id` 0x0002), then according to Hybrid Public Key Encryption, `Nenc` and `Nt` would be 97 and 16, respectively.
WrappedKey type
AES-256-GCM is used for all wrapping and unwrapping.
Table: WrappedKey contents {#wrapped-key-contents}
| Name | Type | Description |
|---|---|---|
| key_type | u16 | Type of the wrapped key. - 0h: Reserved - 1h: LOCKED_MPK (ciphertext held at rest) - 2h: ENABLED_MPK (ciphertext held in RAM) - 3h: WRAPPED_MEK - 4h to FFFFh: Reserved |
| reserved | u16 | Reserved. |
| salt | u8[12] | Random salt for the given wrapped key. |
| metadata_len | u32 | Length of the metadata field. |
| key_len | u32 | Length of the encrypted key. |
| iv | u8[12] | Initialization vector for AES operation. |
| metadata | u8[metadata_len] | Metadata associated with the wrapped key. |
| ciphertext | u8[key_len+16] | Key ciphertext and authentication tag. |
The AAD for the encrypted message is constructed as `key_type` || `salt` || `metadata_len` || `metadata`.
Variants of WrappedKey will be used to reduce duplicating information in commands. The following names will be used for WrappedKeys of a specific `key_type` and `key_len`:
Table: WrappedKey variants {#wrapped-key-variants}
| Name | key_type | key_len |
|---|---|---|
| LockedMpk | LOCKED_MPK | 32 |
| EnabledMpk | ENABLED_MPK | 32 |
| WrappedMek | WRAPPED_MEK | 64 |
Fault handling
A KMB mailbox command can fail to complete in the following ways:
- An ill-formed command.
- Encryption engine timeout.
- Encryption engine reported error.
In all of these cases, the error is reported in the command return status.
KMB mailbox errors should generally result in an error being surfaced to the host.
Table: KMB mailbox command result codes {#kmb-mailbox-codes}
| Name | Value | Description |
|---|---|---|
| LOCK_ENGINE_TIMEOUT | 0x4C45_544F ("LETO") | Timeout occurred when communicating with the drive encryption engine to execute a command. |
| LOCK_ENGINE_ERR + u8 | 0x4C45_52xx ("LERx") | The low byte indicates the error code and ready state. - Bit 0: RDY field of the encryption engine's CTRL register. - Bits [3:1]: reserved - Bits [7:4]: ERR field of the encryption engine's CTRL register. See Table for an enumeration of code values. |
| LOCK_BAD_ALGORITHM | 0x4C42_414C ("LBAL") | Unsupported algorithm, or algorithm does not match the given handle. |
| LOCK_BAD_HANDLE | 0x4C42_4841 ("LBHA") | Unknown handle. |
| LOCK_KEM_DECAPSULATION | 0x4C4B_4445 ("LKDE") | Error during KEM decapsulation. |
| LOCK_ACCESS_KEY_UNWRAP | 0x4C41_4B55 ("LAKU") | Error during access key decryption. |
| LOCK_MPK_DECRYPT | 0x4C50_4445 ("LPDE") | Error during MPK decryption. |
| LOCK_MEK_DECRYPT | 0x4C4D_4445 ("LMDE") | Error during MEK decryption. |
| LOCK_MEK_CHKSUM_FAIL | 0x4C4D_4346 ("LMCF") | Error during MEK derivation due to checksum mismatch. |
| LOCK_HEK_NOT_AVAILABLE | 0x4C48_4E41 ("LHNA") | The operation requires the HEK, which is unavailable. |
| LOCK_MEK_NOT_INITIALIZED | 0x4C4D_4E49 ("LMNI") | MIX_MPK, GENERATE_MEK, LOAD_MEK, or DERIVE_MEK were called without first invoking INITIALIZE_MEK_SECRET. |
Host APIs
In addition to supporting TCG Opal, Enterprise, and Key Per I/O, OCP L.O.C.K. provides underlying support for the Media Encryption Key Multiparty Authorization (MEK-MPA) Opal feature-set TCG Storage Feature Set: Media Encryption Key Multiparty Authorization. These bindings are described in Table.
Table: Mapping between KMB mailbox commands and host-facing storage APIs {#host-api-mapping}
| KMB mailbox command | Related host-facing API | Description |
|---|---|---|
| ENUMERATE_HPKE_HANDLES | TCG MEK-MPA | The HPKE public key certificates reported in the Certificate table are enumerated internally by this command. |
| ENDORSE_HPKE_PUB_KEY | TCG MEK-MPA | The HPKE public key certificates reported in the Certificate table are produced by this command. |
| ROTATE_HPKE_KEY | TCG MEK-MPA | When GenKey is invoked on an HPKE Certificate table entry, the key is rotated via this command. |
| GENERATE_MPK | TCG MEK-MPA | When an AccessCondition is initialized, via InitializeAccessCondition, its key material is produced by this command. The MPK's metadata must be set to the AccessCondition's UID. The SealedAccessKey's `info` must be set to the concatenation of the hex value of the InitializeAccessCondition method UID and the hex value of the target AccessCondition object UID. |
| REWRAP_MPK | TCG MEK-MPA | When an AccessCondition's access key is changed via ChangeAccessKey, the rotation is accomplished via this command. The SealedAccessKey's `info` must be set to the concatenation of the hex value of the ChangeAccessKey method UID and the hex value of the target AccessCondition object UID. |
| ENABLE_MPK | TCG MEK-MPA | When an AccessCondition is enabled via EnableAccess, that action is performed by this command. The SealedAccessKey's `info` must be set to the concatenation of the hex value of the EnableAccess method UID and the hex value of the target AccessCondition object UID. |
| TEST_ACCESS_KEY | TCG MEK-MPA | When an AccessCondition's access key is tested via TestAccessKey, that action is performed by this command. The SealedAccessKey's `info` must be set to the concatenation of the hex value of the TestAccessKey method UID and the hex value of the target AccessCondition object UID. |
| MIX_MPK | TCG MEK-MPA | When a K_AES_* entry is created or unlocked, and that entry is bound to an AccessCondition, the K_AES_* entry is bound to the AccessCondition via this command. |
| GENERATE_MEK | TCG Opal or Enterprise | When a K_AES_* entry is created, that action may be performed by this command. |
| LOAD_MEK | TCG Opal or Enterprise | When a K_AES_* entry is unlocked, that action may be performed by this command. |
| DERIVE_MEK | TCG Opal or Enterprise | When a K_AES_* entry is unlocked, that action may be performed by this command. |
| DERIVE_MEK | TCG Key Per I/O | When a key tag with an associated DEK is injected, the MEK is derived via this command. |
Rotation of the HEK and SEK and injection of host entropy require additional host APIs beyond those available in TCG Opal, Enterprise, or Key Per I/O. Such APIs are beyond the scope of the present document.
Terminology
Table: Acronyms and abbreviations used throughout this document {#abbreviations}
| Abbreviation | Description |
|---|---|
| AES | Advanced Encryption Standard |
| CSP | Critical Security Parameter |
| DPK | Data Protection Key |
| DICE | Device Identifier Composition Engine |
| DRBG | Deterministic Random Bit Generator |
| ECDH | Elliptic-curve DiffieβHellman |
| ECDSA | Elliptic Curve Digital Signature Algorithm |
| EPK | Epoch Protection Key |
| HEK | Hard Epoch Key |
| HMAC | Hash-Based Message Authentication Code |
| HPKE | Hybrid Public Key Encryption |
| IETF EAT | IETF Entity Attestation Token |
| KDF | Key Derivation Function |
| KEM | Key Encapsulation Mechanism |
| KMB | Key Management Block |
| L.O.C.K. | Layered Open-Source Cryptographic Key-management |
| MDK | MEK Deobfuscation Key |
| MEK | Media Encryption Key |
| ML-DSA | Module-Lattice-Based Digital Signature Algorithm |
| ML-KEM | Module-Lattice-Based Key-Encapsulation Mechanism |
| MPK | Multi-party Protection Key |
| NIST | National Institute of Standards and Technology |
| OCP | Open Compute Project |
| RTL | Register Transfer Level |
| SED | Self-encrypting drive |
| SEK | Soft Epoch Key |
| SSD | Solid-state drive |
| TCG | Trusted Computing Group |
| TRNG | True Random Number Generator |
| UART | Universal asynchronous receiver-transmitter |
| VEK | Volatile Escrow Key |
| XTS | XEX-based tweaked-codebook mode with ciphertext stealing |
Compliance
This section enumerates requirements for devices that integrate OCP L.O.C.K.
Table: Compliance requirements {#compliance-requirements}
| Item | Requirement | Mandatory |
|---|---|---|
| 1 | The device shall integrate Caliptra. | Yes |
| 2 | OCP L.O.C.K. shall be enabled by setting the input strap ss_ocp_lock_en in Caliptra Core or cptra_ss_strap_ocp_lock_en_i in Caliptra Subsystem. | Yes |
| 3 | When a storage device is in a production lifecycle state, MEKs shall only be programmable to the encryption engine via Caliptra. See Section. | Yes |
| 4 | The encryption engine shall remove all MEKs from the encryption engine on a power cycle or during zeroization of the storage device. See Section. | Yes |
| 5 | When the device is shipped it must be in the production lifecycle state and have a randomized HEK. See Section. | Yes |
| 6 | MCU ROM shall populate the HEK seed fuse register and invoke REPORT_HEK_METADATA upon cold reset. See Sections [-Section] and [-Section]. | Yes |
| 7 | HEK and SEK programming/zeroization shall adhere to the sequence described in Section. | Yes |
| 8 | MEKs shall not be programmed while the SEK or HEK are zeroized. See Section. | Yes |
| 9 | The drive shall only provide HEK seeds or SEKs that are cryptographically-strong random values. See Section. | Yes |
| 10 | Drive firmware shall implement authorization controls to gate lifecycle events that have the potential to trigger data loss. See Section. | Yes |
| 11 | The encryption engine shall ensure that AES-XTS Key_1 and Key_2 are not equal. See Section. | Yes |
| 12 | KMB shall have access to the SFRs defined in Table through the address defined by OCP_LOCK_MEK_ADDRESS. See Section. | Yes |
| 13 | MCU ROM shall configure registers SS_KEY_RELEASE_BASE_ADDR_L, SS_KEY_RELEASE_BASE_ADDR_H and SS_KEY_RELEASE_SIZE, then setting CPTRA_FUSE_WR_DONE to prevent further modifications. Additionally, OCP_LOCK_MEK_LENGTH must be set to 40h. See Section. | Yes |
| 14 | Upon KMB cold reset or firmware update reset, drive firmware shall enumerate HPKE keypairs supported by KMB and advertise them to the host, if drive firmware supports a host-facing API for doing so. See Section. | Yes |
| 15 | Integrations shall invoke Caliptra resets according to the directives given in Section. | Yes |
Repository location
See https://github.com/chipsalliance/Caliptra/tree/main/doc/ocp_lock.
Preconditioned AES-Encrypt calculations
This appendix expands on Section and provides additional details behind the claim that approximately \(2^{80}\) preconditioned AES-Encrypt operations for a given encryption key are needed before an IV collision is expected to occur with probability greater than \(2^{-32}\).
To satisfy FIPS, it is sufficient to demonstrate that no single AES-GCM key will be used more than \(2^{32}\) times. With a 96-bit salt, a given encryption key can be expanded to up to \(2^{96}\) possible subkeys. This can be put in terms of the "Balls into bins" problem Balls into bins problem, where we are looking for the number of balls (\(m\)) required for the maximum load across \(2^{96}\) bins (\(n\)) to be \(2^{32}\). An approximation for the maximum load where \(m>n\) is given as \(m/n+\sqrt{mΒ·log(n)/n}\). Rearranging to solve for \(m\) via the quadratic formula yields \(mβ2^{128}\). Therefore a given encryption key may be used in at most \(2^{128}\) preconditioned AES-Encrypt operations before any subkey derived from that key is used in more than \(2^{32}\) AES-GCM-Encrypt operations.
However, the \(2^{32}\) encryption limit is a means to an end, namely ensuring a low probability of IV collisions. The Birthday paradox Birthday problem implies that the probability of a collision between \(n\) generated IVs where \(d\) is the number of possible IVs is approximately \(n^2/(2d)\). The chances of a 96-bit IV collision across \(2^{32}\) IVs is therefore approximately \(2^{2Β·32}/2^{96+1}=2^{-33}\) for a given subkey. If each of the \(2^{96}\) derived subkeys might be used up to \(2^{32}\) times, the expected number of derived subkeys that experience a collision is approximately \(2^{96}Β·2^{-33}=2^{63}\). Clearly a limit of \(2^{128}\) encryption operations for a given encryption key, while meeting the letter of the FIPS requirements, is too large for safety.
We can calculate the safe margin by simply considering the 96-bit salt concatenated to the 96-bit IV. What we are really after is the maximum number of preconditioned AES-Encrypt invocations with a given encryption key such that the likelihood of experiencing a collision of the 192-bit (salt || IV) pair is at most \(2^{-32}\). Leveraging the Birthday paradox equation where \(p(n,d)βn^2/(2d)\), \(nβ\sqrt{2dΒ·p}=\sqrt{2^{192+1}Β·2^{-32}}=2^{80.5}\).
Therefore a given encryption key may safely be used in at most \(2^{80.5}\) preconditioned AES-Encrypt operations before an IV collision is expected to occur with probability greater than \(2^{-32}\) across all of the AES-GCM subkeys derived from the encryption key.
EAT format for attesting to the epoch key state
This format is currently under development within TCG as part of the Hard Cryptographic Purge feature set4.
https://trustedcomputinggroup.org/trusted-computing-group-storage-work-group-call-for-participation/
76a7d85

Caliptra Hardware Specification
Revision 2.1
Scope
This document defines technical specifications for a Caliptra RoT for Measurement (RTM)[1] cryptographic subsystem used in the Open Compute Project (OCP). This document, along with Caliptra: A Datacenter System on a Chip (SoC) Root of Trust (RoT), shall comprise the Caliptra technical specification.
Overview
This document provides definitions and requirements for a Caliptra cryptographic subsystem. The document then relates these definitions to existing technologies, enabling device and platform vendors to better understand those technologies in trusted computing terms.
Caliptra Core
For information on the Caliptra Core, see the High level architecture section of Caliptra: A Datacenter System on a Chip (SoC) Root of Trust (RoT).
Key Caliptra Core 2.0 Changes
- AXI subordinate replaces APB interface of Caliptra 1.x hardware
- SHA Accelerator functionality now available exclusively to Caliptra
- Caliptra uC may use internally in mailbox mode or via the Caliptra AXI DMA assist engine in streaming mode
- SHA Accelerator adds new SHA save/restore functionality
- Adams Bridge Dilithium/ML-DSA (refer to Adams bridge spec)
- Subsystem mode support (refer to Subsystem Specification for details)
- ECDH hardware support
- HMAC512 hardware support
- AXI Manager with DMA support (refer to DMA Specification)
- Manufacturing and Debug Unlock
- UDS programming
- Read logic for Secret Fuses
- Streaming Boot Support
- RISC-V core PMP support
- CSR HMAC key for manufacturing flow
Key Caliptra 2.1 Changes
- AXI Manager DMA AES feature for OCP L.O.C.K. support (refer to DMA Specification)
- AES Big Endian mode
- External Staging Area
- OCP LOCK Support
- SHA3
- ML-KEM
Boot FSM
The Boot FSM detects that the SoC is bringing Caliptra out of reset. Part of this flow involves signaling to the SoC that Caliptra is awake and ready for fuses. After fuses are populated and the SoC indicates that it is done downloading fuses, Caliptra can wake up the rest of the IP by de-asserting the internal reset.
The following figure shows the state transitions and associated actions in Caliptra's boot state machine.
Figure: Caliptra Boot FSM state diagram

The Boot FSM first waits for the SoC to assert cptra_pwrgood and de-assert cptra_rst_b. In the BOOT_FUSE state, Caliptra signals to the SoC that it is ready for fuses. After the SoC is done writing fuses, it sets the fuse done register and the FSM advances to BOOT_DONE.
Once in the BOOT_DONE state, Caliptra de-asserts resets through a two flip-flop synchronizer.
FW update reset (Impactless FW update)
When a firmware update is initiated, Runtime FW writes to fw_update_reset register to trigger the FW update reset. When this register is written, only the RISC-V core is reset using cptra_uc_rst_b pin and all AHB targets are still active. All registers within the targets and ICCM/DCCM memories are intact after the reset. Reset is deasserted synchronously after a programmable number of cycles; the minimum allowed number of wait cycles is 5, which is also the default configured value. Reset de-assertion is done through a two flip-flop synchronizer. Since ICCM is locked during runtime, the boot FSM unlocks it when the RISC-V reset is asserted. Following FW update reset deassertion, normal boot flow updates the ICCM with the new FW from the mailbox SRAM.
Impactless firmware updates may be initiated by writing to the fw_update_reset register after Caliptra comes out of global reset and enters the BOOT_DONE state. In the BOOT_FWRST state, only the reset to the RISC-V core is asserted and the wait timer is initialized. After the timer expires, the FSM advances from the BOOT_WAIT to BOOT_DONE state where the reset is deasserted and ICCM is unlocked.
Breakpoints for Debug
Integrators may connect a breakpoint input to Caliptra, which is intended to connect to a chip GPIO pin. When asserted, this pin causes the Caliptra boot FSM to follow a modified arc. Instead of transitioning immediately to the BOOT_DONE state upon completion of fuse programming, the state machine transitions from BOOT_FUSE to BOOT_WAIT. Here, the state machine halts until the Caliptra register CPTRA_BOOTFSM_GO is set, either by AXI or TAP access.
RISC-V core
The RISC-V core is VeeR EL2 from CHIPS Alliance. It is a 32-bit CPU core that contains a 4-stage, scalar, in-order pipeline. The core supports RISC-Vβs integer(I), compressed instruction(C), multiplication and division (M), instruction-fetch fence, CSR, and subset of bit manipulation instructions (Z) extensions. A link to the RISC-V VeeR EL2 Programmerβs Reference Manual is provided in the References section.
Configuration
The RISC-V core is highly configurable and has the following settings.
| Parameter | Configuration |
|---|---|
| Interface | AHB-Lite |
| DCCM | 256 KiB |
| ICCM | 256 KiB |
| I-Cache | Disabled |
| Reset Vector | 0x00000000 |
| Fast Interrupt Redirect | Enabled |
| External Interrupts | 31 |
| PMP | Enabled |
Embedded memory export
Internal RISC-V SRAM memory components are exported from the Caliptra subsystem to support adaptation to various fabrication processes. For more information, see the Caliptra Integration Specification.
Memory map address regions
The 32-bit address region is subdivided into 16 fixed-sized, contiguous 256 MB regions. The following table describes the address mapping for each of the AHB devices that the RISC-V core interfaces with.
| Subsystem | Address size | Start address | End address |
|---|---|---|---|
| ROM | 96 KiB | 0x0000_0000 | 0x0000_BFFF |
| Cryptographic | 512 KiB | 0x1000_0000 | 0x1007_FFFF |
| Peripherals | 32 KiB | 0x2000_0000 | 0x2000_7FFF |
| SoC IFC | 512 KiB | 0x3000_0000 | 0x3007_FFFF |
| RISC-V Core ICCM | 256 KiB | 0x4000_0000 | 0x4003_FFFF |
| RISC-V Core DCCM | 256 KiB | 0x5000_0000 | 0x5003_FFFF |
| RISC-V MM CSR (PIC) | 256 MiB | 0x6000_0000 | 0x6FFF_FFFF |
Cryptographic subsystem
The following table shows the memory map address ranges for each of the IP blocks in the cryptographic subsystem.
| IP/Peripheral | Target # | Address size | Start address | End address |
|---|---|---|---|---|
| Cryptographic Initialization Engine | 0 | 32 KiB | 0x1000_0000 | 0x1000_7FFF |
| ECC Secp384 | 1 | 32 KiB | 0x1000_8000 | 0x1000_FFFF |
| HMAC512 | 2 | 4 KiB | 0x1001_0000 | 0x1001_0FFF |
| Key Vault | 3 | 8 KiB | 0x1001_8000 | 0x1001_9FFF |
| PCR Vault | 4 | 8 KiB | 0x1001_A000 | 0x1001_BFFF |
| Data Vault | 5 | 8 KiB | 0x1001_C000 | 0x1001_DFFF |
| SHA512 | 6 | 32 KiB | 0x1002_0000 | 0x1002_7FFF |
| SHA256 | 10 | 32 KiB | 0x1002_8000 | 0x1002_FFFF |
| ABR (MLDSA/MLKEM) | 14 | 64 KiB | 0x1003_0000 | 0x1003_FFFF |
| AES | 15 | 4 KiB | 0x1001_1000 | 0x1001_1FFF |
| SHA3 | 16 | 4 KiB | 0x1004_0000 | 0x1004_0FFF |
Peripherals subsystem
The following table shows the memory map address ranges for each of the IP blocks in the peripheralsβ subsystem.
| IP/Peripheral | Target # | Address size | Start address | End address |
|---|---|---|---|---|
| CSRNG | 12 | 4 KiB | 0x2000_2000 | 0x2000_2FFF |
| ENTROPY SRC | 13 | 4 KiB | 0x2000_3000 | 0x2000_3FFF |
SoC interface subsystem
The following table shows the memory map address ranges for each of the IP blocks in the SoC interface subsystem.
| IP/Peripheral | Target # | Address size | Start address | End address |
|---|---|---|---|---|
| Mailbox CSR | 7 | 4 KiB | 0x3002_0000 | 0x3002_0FFF |
| SHA512 Accelerator | 7 | 4 KiB | 0x3002_1000 | 0x3002_1FFF |
| AXI DMA | 7 | 4 KiB | 0x3002_2000 | 0x3002_2FFF |
| SOC IFC CSR | 7 | 64 KiB | 0x3003_0000 | 0x3003_FFFF |
| Mailbox SRAM Direct Access | 7 | 256 KiB | 0x3004_0000 | 0x3007_FFFF |
RISC-V core local memory blocks
The following table shows the memory map address ranges for each of the local memory blocks that interface with RISC-V core.
| IP/Peripheral | Target # | Address size | Start address | End address |
|---|---|---|---|---|
| ICCM0 (via DMA) | 9 | 256 KiB | 0x4000_0000 | 0x4003_FFFF |
| DCCM | 8 | 256 KiB | 0x5000_0000 | 0x5003_FFFF |
Interrupts
The VeeR-EL2 processor supports multiple types of interrupts, including non-maskable interrupts (NMI), software interrupts, timer interrupts, external interrupts, and local interrupts. Local interrupts are events not specified by the RISC-V standard, such as auxiliary timers and correctable errors.
Caliptra uses NMI in conjunction with a watchdog timer to support fatal error recovery and system restart. For more information, see the Watchdog timer section.
Software and local interrupts are not implemented in the first generation of Caliptra. Standard RISC-V timer interrupts are implemented using the mtime and mtimecmp registers defined in the RISC-V Privileged Architecture Specification. Both mtime and mtimecmp are included in the soc_ifc register bank, and are accessible by the internal microprocessor to facilitate precise timing tasks. Frequency for the timers is configured by the SoC using the dedicated timer configuration register, which satisfies the requirement prescribed in the RISC-V specification for such a mechanism. These timer registers drive the timer_int pin into the internal microprocessor.
Non-maskable interrupts
Caliptra's RISC-V processor has access to an internal register that allows configuration of the NMI vector. When an NMI occurs, the program counter jumps to the address indicated by the contents of this register. For more information, see NMI Vector.
External interrupts
Caliptra uses the external interrupt feature to support event notification from all attached peripheral components in the subsystem. The RISC-V processor supports multiple priority levels (ranging from 1-15), which allows firmware to configure interrupt priority per component.
Errors and notifications are allocated as interrupt events for each component, with error interrupts assigned a higher priority and expected to be infrequent.
Notification interrupts are used to alert the processor of normal operation activity, such as completion of requested operations or arrival of SoC requests through the shared interface.
Vector 0 is reserved by the RISC-V processor and may not be used, so vector assignment begins with Vector 1. Bit 0 of the interrupt port to the processor corresponds with Vector 1. The following table shows assignment of interrupt vectors to the corresponding IP block. The illustrated interrupt priority assignment is only an example, and does not correspond with actual priorities assigned in the final Caliptra firmware. These interrupt priorities are used in the validation firmware that tests the RTL, and are defined in caliptra_defines.h.
| IP/Peripheral | Interrupt vector | Interrupt priority example (Increasing, Max 15) |
|---|---|---|
| Cryptographic Initialization Engine (Errors) | 1 | 8 |
| Cryptographic Initialization Engine (Notifications) | 2 | 7 |
| ECC (Errors) | 3 | 8 |
| ECC (Notifications) | 4 | 7 |
| HMAC (Errors) | 5 | 8 |
| HMAC (Notifications) | 6 | 7 |
| Key Vault (Errors) | 7 | 8 |
| Key Vault (Notifications) | 8 | 7 |
| SHA512 (Errors) | 9 | 8 |
| SHA512 (Notifications) | 10 | 7 |
| SHA256 (Errors) | 11 | 8 |
| SHA256 (Notifications) | 12 | 7 |
| RESERVED | 13, 15, 17 | 4 |
| RESERVED | 14, 16, 18 | 3 |
| Mailbox (Errors) | 19 | 8 |
| Mailbox (Notifications) | 20 | 7 |
| SHA512 Accelerator (Errors) | 23 | 8 |
| SHA512 Accelerator (Notifications) | 24 | 7 |
| ABR (MLDSA/MLKEM) (Errors) | 23 | 8 |
| ABR (MLDSA/MLKEM) (Notifications) | 24 | 7 |
| AXI DMA (Errors) | 25 | 8 |
| AXI DMA (Notifications) | 26 | 7 |
Watchdog timer
The primary function of Caliptra Watchdog Timer (WDT) is to reset the microcontroller (Caliptra), in the event of a software malfunction, by resetting the device if it has not been cleared in software. It is a two-stage timer, independent of the RISCV core.
Operation
The WDT consists of two timers. When enabled in cascade mode (done by enabling Timer 1 alone), the WDT increments Timer 1 until the counter rolls over or times out. Typically, the timer is serviced at regular intervals to prevent it from overflowing or rolling over. If Timer 1 has not timed out, Timer 2 is disabled and held at its initial value. However, when Timer 1 does roll over, it triggers an error interrupt to the RISC-V core. In parallel, Timer 2 is enabled and begins counting. If the interrupt is serviced before Timer 2 times out, the timers are reset and continue to operate normally. If Timer 2 times out, it asserts an SoC fatal error and an NMI. The SoC fatal error is also captured in the CPTRA_HW_ERROR_FATAL register, which can be cleared by the SoC by writing a 1. A warm reset is required by the SoC to reset the timers when Timer 2 times out.
The WDT timers can be configured to operate independent of each other. When the enable register for Timer 2 is set, the default configuration of cascaded timers is disabled and both timers count independently of each other. In this case, a timeout on Timer 2 causes an error interrupt to the RISC-V core similar to Timer 1. Disabling Timer 2 configures the timers back into the default cascaded mode.
Each timer has an enable bit, a restart bit, and a 64-bit timeout value register that can be programmed as needed. The restart bit is used to service the timers and restart counting. The timeout period registers can be configured to the desired upper bound of timers.
If the WDT timers are disabled and then re-enabled with a new timeout period, they must be restarted by setting the appropriate control register (restart bit). If the timers are temporarily disabled and re-enabled with the same timeout period, they resume counting and do not restart from 0.
For more details regarding the register interface to control the WDT, see the register documentation published in the RTL GitHub repository.
The following figure shows the two timers.
Figure: Caliptra Watchdog Timer

Prescale settings
Assuming a clock source of 500 MHz, a timeout value of 32βhFFFF_FFFF results in a timeout period of ~8.5 seconds. Two 32-bit registers are provided for each timer, allowing a 64-bit timeout period to be programmed for each timer. This accommodates a maximum timeout value of over 1000 years for the same 500 Mhz clock source.
Microcontroller interface
The Caliptra microcontroller communicates with the mailbox through its internal AHB-Lite fabric.
AHB-lite interface
AHB-lite is a subset of the full AHB specification. It is primarily used in single initiator systems. This interface connects VeeR EL2 Core (LSU initiator) to the target devices. See Caliptra Core for information.
The interface can be customized to support variable address and data widths, and a variable number of target devices. Each target device is assigned an address range within the 32-bit address memory map region. The interface includes address decoding logic to route data to the appropriate AHB target device based on the address specified.
The integration parameters for Caliptraβs AHB-lite interface are shown in the following table.
| Parameter | Value |
|---|---|
| ADDRESS_WIDTH | 32 |
| DATA_WIDTH | 64 |
| NUM_OF_SLAVES | 17 |
Each IP component in the Caliptra system uses a native AHB data width of 32-bits (1 dword). The AHB responder logic in each IP component contains width conversion logic that transforms from the fabric data width of 64-bits to this native 32-bit width. The conversion involves calculating the dword offset (either 0 or 1) relative to the native 64-bit width by evaluating bit [2] of the address line. This information is used to extract the correct 32-bits from the native write data line. If there is a data offset, data is shifted down by 32-bits; otherwise, the upper 32-bits are simply truncated. This new dword-address is passed to the internal register interface along with the dword-sized data. A similar conversion works in reverse to correctly place read data in the response data line from the responder.
As a result of this implementation, 64-bit data transfers are not supported on the Caliptra AHB fabric. Firmware running on the internal microprocessor may only access memory and registers using a 32-bit or smaller request size, as 64-bit transfer requests will be corrupted.
All AHB requests internal to Caliptra must be to an address that is aligned to the native data width of 4-bytes. Any AHB read or write by the Caliptra RISC-V processor that is not aligned to this boundary will fail to decode to the targeted register, will fail to write the submitted data, and will return read data of all zeroes. All AHB requests must also use the native size of 4 bytes (encoded in the hsize signal with a value of 2). The only exception to this is when the RISC-V processor performs byte-aligned, single-byte reads to the Mailbox SRAM using the direct-access mechanism described in SoC Mailbox. In this case, a byte-aligned address must be accompanied by the correct size indicator for a single-byte access. Read addresses for byte accesses are aligned to the 4-byte boundary in hardware, and will successfully complete with the correct data at the specified byte offset. Direct mode SRAM writes must be 4-bytes in size and must be aligned to the 4-byte boundary. Hardware writes the entire dword of data to the aligned address, so attempts to write a partial word of data may result in data corruption.
Cryptographic subsystem
For details, see the Cryptographic subsystem architecture section.
SoC mailbox
For more information on the mailbox protocol, see Mailbox in the Caliptra Integration Specification. Mailbox registers accessible to the Caliptra microcontroller are defined in internal-regs/mbox_csr.
The RISC-V processor is able to access the SoC mailbox SRAM using a direct access mode (which bypasses the defined mailbox protocol). The addresses for performing this access are described in SoC interface subsystem and in mbox_sram. In this mode, firmware must first acquire the mailbox lock. Then, reads and writes to the direct access address region will go directly to the SRAM block. Firmware must release the mailbox lock by writing to the mbox_unlock register after direct access operations are completed.
Security state
Caliptra uses the MSB of the security state input to determine whether or not Caliptra is in debug mode.
When Caliptra is in debug mode:
-
Security state MSB is set to 0.
-
Caliptra JTAG is opened for the microcontroller and HW debug.
-
Device secrets (UDS, FE, key vault, csr hmac key and obfuscation key) are programmed to debug values.
If a transition to debug mode happens during ROM operation, any values computed from the use of device secrets may not match expected values.
Transitions to debug mode trigger a hardware clear of all device secrets, and also trigger an interrupt to FW to inform of the transition. FW is responsible for initiating another hardware clear of device secrets utilizing the clear secrets register, in case any derivations were in progress and stored after the transition was detected. FW may open the JTAG after all secrets are cleared.
Debug mode values may be set by integrators in the Caliptra configuration files. The default values are shown in the following table.
| Name | Default value |
|---|---|
| Obfuscation Key Debug Value | All 0x1 |
| CSR HMAC Key Debug Value | All 0x1 |
| UDS Debug Value | All 0x1 |
| Field Entropy Debug Value | All 0x1 |
| Key Vault Debug Value 0 | All 0xA |
| Key Vault Debug Value 1 | All 0x5 |
Note: When entering debug or scan mode, all crypto engines are zeroized. Before starting any crypto operation in these modes, the status registers of all crypto engines must be checked to confirm they are ready. Failing to do so may trigger a fatal error caused by concurrent crypto operations.
Clock gating
Caliptra provides a clock gating feature that turns off clocks when the microcontroller is halted. Clock gating is disabled by default, but can be globally enabled via the following register.
| Control register | Start address | Description |
|---|---|---|
| CPTRA_CLK_GATING_EN | 0x300300bc | Register bit to enable or disable the clock gating feature. |
When enabled, halting the microcontroller turns off clocks to all of the cryptographic subsystem, the vaults (key vault, PCR vault, and data vault), mailbox SRAM, SoC interface, and peripherals subsystem. The Watchdog timer and SoC registers run on the gated RDC clock. The RV core implements its own clock gating mechanism. Halting the core automatically turns off its clock.
There are a total of 4 clocks in Caliptra: ungated clock, gated clock, gated RDC clock, and gated SoC IFC clock. The following table shows the different modules and their designated clocks.
| Module | Clock |
|---|---|
| RV core | Clk |
| SOC IFC | Clk; clk_cg; rdc_clk_cg; soc_ifc_clk_cg |
| Crypto subsystem | Clk_cg |
| Vaults | Clk_cg |
| Peripherals subsystem | Clk_cg |
| AHB Lite IF, 2to1 Mux | Clk_cg |
| TRNG | Clk_cg |
Wake up conditions
For details on halting the core and waking up the core from the halt state, see section 5 of the RISC-V VeeR EL2 Programmer's Reference Manual.
When the RV core wakes up, all clocks are enabled. However, when the core is halted, it is possible to wake up Caliptra clocks through:
-
A change in generic_input_wires
-
Cptra_fatal_error assertion
-
Entry to debug or scan modes
-
JTAG accesses
-
AXI transactions
Activity on the AXI subordinate interface only wakes up the SoC IFC clock. All other clocks remain off until any other condition is met or the core exits the halt state.
| Cpu_halt_status | s_axi_active | Generic input wires || fatal error || debug/scan mode ||JTAG access | Expected behavior |
|---|---|---|---|
| 0 | X | X | All gated clocks active |
| 1 | 0 | 0 | All gated clocks inactive |
| 1 | 0 | 1 | All gated clocks active (as long as condition is true) |
| 1 | 1 | 0 | Soc_ifc_clk_cg active (as long as s_axi_active = 1) All other clks inactive |
| 1 | 1 | 1 | Soc_ifc_clk_cg active (as long as condition is true OR s_axi_active = 1) All other clks active (as long as condition is true) |
Usage
The following applies to the clock gating feature:
- The core should only be halted after all pending vault writes are done and cryptographic operations are complete.
- While the core is halted, any AXI transaction wakes up the SoC interface clock and leaves all other clocks disabled. If the core is still halted when the AXI transactions are done, the SoC interface clock is returned to a disabled state. .
- The RDC clock is similar to an ungated clock and is only disabled when a reset event occurs. This avoids metastability on flops. The RDC clock operates independently of core halt status.
Timing information
The following figure shows the timing information for clock gating.
Figure: Clock gating timing

Integrated TRNG
Caliptra implements a true random number generator (TRNG) block for local use models. Firmware is able to read a random number from the TRNG core by accessing its register block over the AHB-lite interface. This is a configuration that SoC integrators enable by defining CALIPTRA_INTERNAL_TRNG.
This TRNG block is a combination of entropy source and CSRNG implementations. For information, see the ENTROPY_SRC HWIP Technical Specification and the CSRNG HWIP Technical Specification. The core code (see entropy source and csrng) is reused from here but the interface to the module is changed to AHB-lite. This design provides an interface to an external physical random noise generator. This is also referred to as a physical true random number generator (PTRNG). The PTRNG external source is a physical true random noise source. A noise source and its relation to an entropy source are defined by SP 800-90B.
The block is instantiated based on a design parameter chosen at integration time. This is to provide options for SoC to reuse an existing TRNG to build an optimized SoC design. For the optimized scenarios, SoC needs to follow the External-TRNG REQ HW API.
The following figure shows the integrated TRNG block.
Figure: Integrated TRNG block

The following figure shows the CSRNG block.
Figure: CSRNG block

The following figure shows the entropy source block.
Figure: Entropy source block

Operation
Requests for entropy bits start with command requests over the AHB-lite interface to the csrng CMD_REQ register.
The following describes the fields of the command request header:
-
Application Command: Selects one of five operations to perform. The commands supported are instantiate, reseed, generate, update, and uninstantiate.
-
Command Length: Number of 32-bit words that can optionally be appended to the command. A value of zero will only transfer the command header. A value of 4'hc transfers the header plus an additional twelve 32-bit words of data.
-
Command Flag0: flag0 is associated with the current command. Setting this field to True (4βh6) enables flag0 to be enabled. Note that flag0 is used for the instantiate and reseed commands only; for all other commands, the flag0 value is ignored.
-
Generate Length: Only defined for the generate command, this field is the total number of cryptographic entropy blocks requested. Each unit represents 128 bits of entropy returned. A value of 8 would return a total of 1024 bits. The maximum size supported is 4096.
First an instantiate command is requested over the SW application interface to initialize an instance in the CSRNG module. Depending on the flag0 and clen fields in the command header, a request to the entropy_src module over the entropy interface is sent to seed the csrng. This can take a few milliseconds if the seed entropy is not immediately available.
Example instantiation:
acmd = 0x1 (Instantiate)
clen/flag0 = The seed behavior is described in the following table.
glen = Not used
| flag0 | clen | Description |
|---|---|---|
| F | 0 | Only entropy source seed is used. |
| F | 1-12 | Entropy source seed is xor'ed with provided additional data. |
| T | 0 | Seed of zero is used (no entropy source seed used). |
| T | 1-12 | Only provided additional data is used as seed. |
Next a generate command is used to request generation of cryptographic entropy bits. The glen field defines how many 128 bit words are to be returned to the application interface. After the generated bits are ready, they can be read out via the GENBITS register. This register must be read out glen * 4 times for each request made.
Example generate command:
acmd = 0x3 (Generate)
clen = 0
flag0 = false (4βh9)
glen = 4 (4 *128 = 512b)
This requires 16 reads from GENBITS to read out all of the generated entropy.
Configuration
The HW application interfaces are not supported. Only the SW application interface should be used for this design.
Physical true random noise source signal descriptions
These are the top level signals defined in caliptra_top.
| Name | Input or output | Description |
|---|---|---|
| itrng_data | input | Physical true random noise source data |
| itrng_valid | input | Valid is asserted high for one cycle when data is valid. The expected valid output rate is about 50KHz. |
The following figure shows the top level signals defined in caliptra_top.
Figure: caliptra_top signals

Entropy source signal descriptions
The following table provides descriptions of the entropy source signals.
| Name | Input or output | Description |
|---|---|---|
| clk_i | input | All signal timings are related to the rising edge of clk. |
| rst_ni | input | The reset signal is active LOW and resets the core. |
| entropy_src_rng_req | output | Request from the entropy_src module to the physical true random noise source to start generating data. |
| entropy_src_rng_rsp | input | Contains the internal TRNG data and a flag indicating the data is valid. Valid is asserted high for one cycle when data is valid. |
| entropy_src_hw_if_i | input | Downstream block request for entropy bits. |
| entropy_src_hw_if_o | output | 384 bits of entropy data. Valid when es_ack is asserted high. |
| cs_aes_halt_i | input | Response from csrng that all requests to AES block are halted. |
| cs_aes_halt_o | output | Request to csrng to halt requests to the AES block for power leveling purposes. |
The following figure shows the entropy source signals.
Figure: Entropy source signals

CSRNG signal descriptions
The following table provides descriptions for the CSRNG signals.
| Name | Input or output | Description |
|---|---|---|
| clk_i | input | All signal timings are related to the rising edge of clk. |
| rst_ni | input | The reset signal is active LOW and resets the core. |
| otp_en_csrng_sw_app_read_i | input | Enable firmware to access the ctr_drbg internal state and genbits through registers. |
| lc_hw_debug_en_i | input | Lifecycle that selects which diversification value is used for xoring with the seed from entropy_src. |
| entropy_src_hw_if_i | input | 384 bits of entropy data. Valid when es_ack is asserted high. |
| entropy_src_hw_if_o | output | Downstream block request for entropy bits. |
| cs_aes_halt_i | input | Request from entropy_src to halt requests to the AES block for power leveling purposes. |
| cs_aes_halt_o | output | Response to entropy_src that all requests to AES block are halted. |
The CSRNG may only be enabled if entropy_src is enabled. After it is disabled, CSRNG may only be re-enabled after entropy_src has been disabled and re-enabled.
FIPS considerations
The following sections illustrate the self-test parameter configuration. The
entropy_src block provides additional tests, but Caliptra focuses primarily
on the adaptive and repetition count tests, which are the ones strictly
required for FIPS compliance. Additional details can be found in NIST
publication SP 800-90B.
The TRNG must be re-initialized whenever self-test parameter changes are needed. As described in the previous section, the initialization steps are as follows:
- Disable
csrngandentropy_srcin that order. - Apply new self-test configuration.
- Enable
entropy_srcandcsrngin that order.
Adaptive self-test window and thresholds
This section details the configuration of the entropy_src, focusing on how
the test window size for the adaptive self-test is determined and how it
relates to threshold calculations.
Understanding Test Window Sizes
The adaptive self-test within the entropy_src block utilizes a
configurable test window. To clarify its interpretation, two terms are
defined:
ENTROPY_TEST_WINDOW: This refers to the test window size directly configured in the hardware registers of theentropy_srcblock.ACTUAL_TEST_WINDOW: This refers to the effective window size used for the adaptive self-test threshold calculations. Its value depends on how the test scores are aggregated.
The aggregation method is determined by the CONF.THRESHOLD_SCOPE setting in the entropy_src block.
Aggregate per symbol
When CONF.THRESHOLD_SCOPE is enabled:
- The adaptive test combines the inputs from all physical entropy lines into a single, cumulative score.
- The test essentially treats the combined input as a single binary stream, counting the occurrences of '1's.
- In this configuration:
- If
ENTROPY_TEST_WINDOWis set to 1024, then ACTUAL_TEST_WINDOW=ENTROPY_TEST_WINDOW= 1024
- If
Handle each physical noise source separately
When CONF.THRESHOLD_SCOPE is disabled:
- The adaptive test scores each individual physical noise input line independently.
- This allows for monitoring the health of each noise source.
- In this configuration (assuming, for example, 4 noise sources):
- If
ENTROPY_TEST_WINDOWis set to 4096 bits, then ACTUAL_TEST_WINDOW= (ENTROPY_TEST_WINDOW/ 4) = 1024
- If
Configuring adaptive self-test thresholds
Once the ACTUAL_TEST_WINDOW is determined, the adaptive self-test
thresholds can be configured as follows:
ADAPTP_HI_THRESHOLDS.FIPS_THRESH=adaptp_cutoffADAPTP_LO_THRESHOLDS.FIPS_THRESH=ACTUAL_TEST_WINDOW-adaptp_cutoff
Here, adaptp_cutoff represents the pre-determined cutoff value for the
adaptive proportion test, as defined by NIST SP 800-90B. See the threshold
calculations below as an example.
\(Ξ± = 2^{-40}\) (recommended)
\(H = 0.5\) (example, estimated entropy measured from hardware)
\(W\) = ACTUAL_TEST_WINDOW
adaptp_cutoff = \(1 + critbinom(W, 2^{-H}, 1 - Ξ±)\)
Note: The
critbinomfunction (critical binomial distribution function) is implemented by most spreadsheet applications.
Recommended configuration
The following configuration is recommended for the adaptive and repetition count tests:
Adaptive test
- Set
CONF.THRESHOLD_SCOPEto disabled. This allows the test to monitor and score each physical noise source individually, providing more granular health information. - Set
HEALTH_TEST_WINDOWS.FIPS_WINDOWto 4096 bits. This value serves as theENTROPY_TEST_WINDOW. With the current 4 noise source configuration, this is equivalent to 1024 bits per noise source, where each source produces 1 bit of entropy as defined in NIST SP 800-90B. - Calculate thresholds. Use an
ACTUAL_TEST_WINDOWof 1024 bits (derived from step 2) in the adaptive test threshold formulas provided earlier in this subsection.
Repetition count test
The methodology used for calculating the repetition count threshold in the ROM boot phase can be directly applied for this test as well. The threshold is applied on a per-noise-source basis.
External-TRNG REQ HW API
For SoCs that choose to not instantiate Caliptraβs integrated TRNG, Caliptra provides a TRNGREQ HW API.
- Caliptra asserts TRNG_REQ wire (FW made the request for a TRNG).
- SoC writes the TRNG architectural registers.
- SoC writes a done bit in the TRNG architectural registers.
- Caliptra desserts TRNG_REQ.
The reason to have a separate interface from the SoC mailbox is to ensure that this request is not intercepted by any SoC FW agents that communicate with the SoC mailbox. It is required for FIPS compliance that this TRNG HW API is always handled by a SoC HW gasket logic and not handled by SoC ROM/FW code.
SoC-SHA accelerator HW API
Caliptra provides a SHA accelerator HW API for Caliptra internal FW to use via mailbox or via DMA operations through the AXI subordinate interface. The SHA accelerator HW API is restricted on AXI for use by Caliptra via the AXI DMA assist block; this access restriction is enforced by checking logic on the AXI AxUSER signal associated with the request.
Using the HW API:
- A user of the HW API first locks the accelerator by reading the LOCK register. A read that returns the value 0 indicates that the resource was locked for exclusive use by the requesting user. A write of β1 clears the lock.
- The USER register captures the AXI USERID value of the requestor that locked the SHA accelerator. This is the only user that is allowed to control the SHA accelerator by performing AXI register writes. Writes by any other agent on the AXI subordinate interface are dropped.
- SHA supports Mailbox mode: SHA is computed on LENGTH (DLEN) bytes of data stored in the mailbox beginning at START_ADDRESS. This computation is performed when the EXECUTE register is set by the user. When the operation is completed and the result in the DIGEST register is valid, SHA accelerator sets the VALID bit of the STATUS register.
- Note that even though the mailbox size is fixed, due to SHA save/restore function enhancement, there is no limit on the size of the block that needs to be SHAd. SOC needs to follow FW API
- The SHA computation engine in the SHA accelerator requires big endian data, but the SHA accelerator can accommodate mailbox input data in either the little endian or big endian format. By default, input data is assumed to be little endian and is swizzled to big endian at the byte level prior to computation. For the big endian format, data is loaded into the SHA engine as-is. Users may configure the SHA accelerator to treat data as big endian by setting the ENDIAN_TOGGLE bit appropriately.
- See the register definition for the encodings.
- SHA engine also provides a βzeroizeβ function through its CONTROL register to clear any of the SHA internal state. This can be used when the user wants to conceal previous state for debug or security reasons.
JTAG implementation
For specific debug flows, see the JTAG/TAP Debug section in Caliptra: A Datacenter System on a Chip (SoC) Root of Trust (RoT).
The following figure shows the JTAG implementation within the Caliptra boundary. The output of the existing DMI wrapper is used to find the non-Core (Caliptra uncore) aperture to route the JTAG commands.
Caliptraβs JTAG/TAP should be implemented as a TAP EP. JTAG is open if the debug mode is set at the time of Caliptra reset deassertion.
Note: If the debug security state switches to debug mode anytime, the security assets and keys are still flushed even though JTAG is not open.
The following table details the alias addresses for registers in soc ifc that are accessible through JTAG. Debug Locked registers are a subset of registers accessible when debug intent is set, when debug is unlocked, or the lifecycle state is DEVICE_MANUFACTURING. Debug Unlocked registers are accessible when debug is unlocked, or the lifecycle state is DEVICE_MANUFACTURING.
| Register Name | JTAG Address | Accessibility | Debug Locked | Debug Unlocked |
|---|---|---|---|---|
| mbox_lock | 7βh75 | RO | YES | YES |
| mbox_cmd | 7βh76 | RW | YES | YES |
| mbox_dlen | 7βh50 | RW | YES | YES |
| mbox_dataout | 7βh51 | RO | YES | YES |
| mbox_datain | 7βh62 | WO | YES | YES |
| mbox_status | 7βh52 | RW | YES | YES |
| mbox_execute | 7βh77 | WO | YES | YES |
| CPTRA_BOOT_STATUS | 7βh53 | RO | YES | YES |
| CPTRA_HW_ERRROR_ENC | 7βh54 | RO | YES | YES |
| CPTRA_FW_ERROR_ENC | 7βh55 | RO | YES | YES |
| SS_UDS_SEED_BASE_ADDR_L | 7βh56 | RO | YES | |
| SS_UDS_SEED_BASE_ADDR_H | 7βh57 | RO | YES | |
| CPTRA_HW_ERROR_FATAL | 7βh58 | RO | YES | YES |
| CPTRA_FW_ERROR_FATAL | 7βh59 | RO | YES | YES |
| CPTRA_HW_ERROR_NON_FATAL | 7βh5a | RO | YES | YES |
| CPTRA_FW_ERROR_NON_FATAL | 7βh5b | RO | YES | YES |
| CPTRA_DBG_MANUF_SERVICE_REG | 7βh60 | RW | YES | YES |
| CPTRA_BOOTFSM_GO | 7βh61 | RW | YES | YES |
| SS_DEBUG_INTENT | 7βh63 | RW | YES | |
| SS_CALIPTRA_BASE_ADDR_L | 7βh64 | RW | YES | |
| SS_CALIPTRA_BASE_ADDR_H | 7βh65 | RW | YES | |
| SS_MCI_BASE_ADDR_L | 7βh66 | RW | YES | |
| SS_MCI_BASE_ADDR_H | 7βh67 | RW | YES | |
| SS_RECOVERY_IFC_BASE_ADDR_L | 7βh68 | RW | YES | |
| SS_RECOVERY_IFC_BASE_ADDR_H | 7βh69 | RW | YES | |
| SS_OTP_FC_BASE_ADDR_L | 7βh6A | RW | YES | |
| SS_OTP_FC_BASE_ADDR_H | 7βh6B | RW | YES | |
| SS_STRAP_GENERIC_0 | 7βh6C | RW | YES | |
| SS_STRAP_GENERIC_1 | 7βh6D | RW | YES | |
| SS_STRAP_GENERIC_2 | 7βh6E | RW | YES | |
| SS_STRAP_GENERIC_3 | 7βh6F | RW | YES | |
| SS_DBG_SERVICE_REG_REQ | 7βh70 | RW | YES | YES |
| SS_DBG_SERVICE_REG_RSP | 7βh71 | RO | YES | YES |
| SS_DBG_UNLOCK_LEVEL0 | 7βh72 | RW | YES | |
| SS_DBG_UNLOCK_LEVEL1 | 7βh73 | RW | YES | |
| SS_STRAP_CALIPTRA_DMA_AXI_USER | 7βh74 | RW | YES | |
| SS_EXTERNAL_STAGING_AREA_BASE_ADDR_L | 7βh78 | RW | YES | |
| SS_EXTERNAL_STAGING_AREA_BASE_ADDR_H | 7βh79 | RW | YES |
Figure: JTAG implementation

Cryptographic subsystem architecture
The architecture of Caliptra cryptographic subsystem includes the following components:
- Symmetric cryptographic primitives
- De-obfuscation engine
- SHA512/384 (based on NIST FIPS 180-4 [2])
- SHA256 (based on NIST FIPS 180-4 [2])
- HMAC512 (based on NIST FIPS 198-1 [5] and RFC 4868 [6])
- SHA3 (based on NIST FIPS 202 [17])
- Public-key cryptography
- NIST Secp384r1 Deterministic Digital Signature Algorithm (based on FIPS-186-4 [11] and RFC 6979 [7])
- Key vault
- Key slots
- Key slot management
The high-level architecture of Caliptra cryptographic subsystem is shown in the following figure.
Figure: Caliptra cryptographic subsystem

SHA512/SHA384
SHA512 is a function of cryptographic hash algorithm SHA-2. The hardware implementation is based on Secworks/SHA512 [1]. This implementation complies with the functionality in NIST FIPS 180-4 [2]. The implementation supports the SHA512 variants SHA-512/224, SHA-512/256, SHA384 and SHA512.
The SHA512 algorithm is described as follows:
- The message is padded by the host and broken into 1024-bit chunks
- For each chunk:
- The message is fed to the SHA512 core
- The core should be triggered by the host
- The SHA512 core status is changed to ready after hash processing
- The result digest can be read after feeding all message chunks
Operation
Padding
The message should be padded before feeding to the hash core. The input message is taken, and some padding bits are appended to it to get it to the desired length. The bits that are used for padding are simply β0β bits with a leading β1β (100000β¦000). The appended length of the message (before pre-processing), in bits, is a 128-bit big-endian integer.
The total size should be equal to 128 bits short of a multiple of 1024 since the goal is to have the formatted message size as a multiple of 1024 bits (N x 1024). The following figure shows the SHA512 input formatting.
Figure: SHA512 input formatting

Hashing
The SHA512 core performs 80 iterative operations to process the hash value of the given message. The algorithm processes each block of 1024 bits from the message using the result from the previous block. For the first block, the initial vectors (IV) are used for starting the chain processing of each 1024-bit block.
FSM
The SHA512 architecture has the finite-state machine as shown in the following figure.
Figure: SHA512 FSM

Signal descriptions
The SHA512 architecture inputs and outputs are described in the following table.
| Name | Inputs and outputs | Description |
|---|---|---|
| clk | input | All signal timings are related to the rising edge of clk. |
| reset_n | input | The reset signal is active LOW and resets the core. This is the only active LOW signal. |
| init | input | The core is initialized and processes the first block of message. |
| next | input | The core processes the rest of the message blocks using the result from the previous blocks. |
| mode[1:0] | input | Indicates the hash type of the function. This can be: - SHA512/224 - SHA512/256 - SHA384 - SHA512 |
| zeroize | input | The core clears all internal registers to avoid any SCA information leakage. |
| block[1023:0] | input | The input padded block of message. |
| ready | output | When HIGH, the signal indicates the core is ready. |
| digest[511:0] | output | The hashed value of the given block. |
| digest_valid | output | When HIGH, the signal indicates that the result is ready. |
Address map
The SHA512 address map is shown here: sha512_reg β clp Reference (chipsalliance.github.io)
Pseudocode
The following pseudocode demonstrates how the SHA512 interface can be implemented.
Figure: SHA512 pseudocode

SCA countermeasure
We do not propose any countermeasure to protect the hash functions. Inherently, hash functions do not work like other cryptographic engines. Hash functions target integrity without requiring a secret key. Hence, the attacker can target only messages. Also, the attacker cannot build a CPA or DPA platform on the hash function because the same message ideally gives the same side-channel behavior.
If the attacker works on the multi-message mechanism, the attacker then needs to work with single trace attacks, which are very unlikely in ASIC/FPGA implementations. Also, our hash implementation is a noisy platform. As a result, we do not propose any SCA implementation countermeasure on the hash functions.
Performance
The SHA512 core performance is reported considering two different architectures: pure hardware architecture, and hardware/software architecture. These are described next.
Pure hardware architecture
In this architecture, the SHA512 interface and controller are implemented in hardware. The performance specification of the SHA512 architecture is shown in the following table.
| Operation | Cycle count [CCs] | Time [us] @ 400 MHz | Throughput [op/s] |
|---|---|---|---|
| Data_In transmission | 0.08 | ||
| Process | 87 | 0.22 | |
| Data_Out transmission | 16 | 0.04 | |
| Single block | 136 | 0.34 | 2,941,176 |
| Double block | 224 | 0.56 | 1,785,714 |
| 1 KiB message | 840 | 2.10 | 476,190 |
| 128 KiB message | 17,632 | 44.08 | 22,686 |
Hardware/software architecture
In this architecture, the SHA512 interface and controller are implemented in RISC-V core. The performance specification of the SHA512 architecture is shown in the following table.
| Operation | Cycle count [CCs] | Time [us]] @ 400 MHz | Throughput [op/s] |
|---|---|---|---|
| Data_In transmission | 990 | 2.48 | |
| Process | 139 | 0.35 | |
| Data_Out transmission | 387 | 0.97 | |
| Single block | 1,516 | 3.79 | 263,852 |
| Double block | 2,506 | 6.27 | 159,617 |
| 1 KiB message | 9,436 | 23.59 | 42,391 |
| 128 KiB message | 1,015,276 | 2,538.19 | 394 |
Pure software architecture
In this architecture, the SHA512 algorithm is implemented fully in software. The implementation is derived from the OpenSSL SHA512 implementation [3]. The performance numbers for this architecture are shown in the following table.
| Data size | Cycle count |
|---|---|
| 1 KiB | 147,002 |
| 4 KiB | 532,615 |
| 8 KiB | 1,046,727 |
| 12 KiB | 1,560,839 |
| 128 KiB | 16,470,055 |
SHA256
SHA256 is a function of the SHA-2 cryptographic hash algorithm. The hardware implementation is based on Secworks/SHA256 [1]. The implementation supports the two variants: SHA256/224 and SHA256.
The SHA256 algorithm is described as follows:
- The message is padded by the host and broken into 512-bit chunks
- For each chunk:
- The message is fed to the sha256 core
- The core should be triggered by the host
- The sha256 core status is changed to ready after hash processing
- The result digest can be read after feeding all message chunks
Operation
Padding
The message should be padded before feeding to the hash core. The input message is taken, and some padding bits are appended to the message to get it to the desired length. The bits that are used for padding are simply β0β bits with a leading β1β (100000β¦000). The appended length of the message (before pre-processing), in bits, is a 64-bit big-endian integer.
The total size should be equal to 64 bits, short of a multiple of 512 because the goal is to have the formatted message size as a multiple of 512 bits (N x 512).
The following figure shows SHA256 input formatting.
Figure: SHA256 input formatting

Hashing
The SHA256 core performs 64 iterative operations to process the hash value of the given message. The algorithm processes each block of 512 bits from the message using the result from the previous block. For the first block, the initial vectors (IV) are used to start the chain processing of each 512-bit block.
FSM
The SHA256 architecture has the finite-state machine as shown in the following figure.
Figure: SHA256 FSM

Signal descriptions
The SHA256 architecture inputs and outputs are described as follows.
| Name | Input or output | Description |
|---|---|---|
| clk | input | All signal timings are related to the rising edge of clk. |
| reset_n | input | The reset signal is active LOW and resets the core. This is the only active LOW signal. |
| init | input | The core is initialized and processes the first block of message. |
| next | input | The core processes the rest of the message blocks using the result from the previous blocks. |
| mode | input | Indicates the hash type of the function. This can be: - SHA256/224 - SHA256 |
| zeroize | input | The core clears all internal registers to avoid any SCA information leakage. |
| WNTZ_MODE* | input | SHA256 core is configured in Winternitz verification mode. |
| WNTZ_W[3:0]* | input | Winternitz W value. |
| WNTZ_N_MODE* | input | Winternitz n value(SHA192/SHA256 --> n = 24/32) |
| block[511:0] | input | The input padded block of message. |
| ready | output | When HIGH, the signal indicates the core is ready. |
| digest[255:0] | output | The hashed value of the given block. |
| digest_valid | output | When HIGH, the signal indicates the result is ready. |
* For more imformation about these inputs, please refer to LMS accelerator section.
Address map
The SHA256 address map is shown here: sha256_reg β clp Reference (chipsalliance.github.io).
Pseudocode
The following pseudocode demonstrates how the SHA256 interface can be implemented.
Figure: SHA256 pseudocode

SCA countermeasure
We do not propose any countermeasure to protect the hash functions. For more information, see SCA countermeasure in the SHA512/SHA384 section.
Performance
The SHA256 core performance is reported considering two different architectures: pure hardware architecture, and hardware/software architecture. These are described next.
Pure hardware architecture
In this architecture, the SHA256 interface and controller are implemented in hardware. The performance specification of the SHA256 architecture is reported as shown in the following table.
| Operation | Cycle count [CCs] | Time [us] @ 400 MHz | Throughput [op/s] |
|---|---|---|---|
| Data_In transmission | 17 | 0.04 | |
| Process | 66 | 0.17 | |
| Data_Out transmission | 8 | 0.02 | |
| Single block | 91 | 0.23 | 4,395,604 |
| Double block | 158 | 0.40 | 2,531,646 |
| 1 KiB message | 1163 | 2.91 | 343,938 |
Hardware/software architecture
In this architecture, the SHA256 interface and controller are implemented in RISC-V core. The performance specification of the SHA256 architecture is reported as shown in the following table.
| Operation | Cycle count [CCs] | Time [us] @ 400 MHz | Throughput [op/s] |
|---|---|---|---|
| Data_In transmission | 500 | 1.25 | |
| Process | 66 | 0.17 | |
| Data_Out transmission | 195 | 0.49 | |
| Single block | 761 | 1.90 | 525,624 |
| Double block | 1355 | 3.39 | 295,203 |
| 1 KiB message | 8761 | 21.90 | 45,657 |
SHA3
The SHA3 HWIP performs the hash functions, whose purpose is to check the integrity of the received message. It supports various SHA3 hashing functions including SHA3 Extended Output Function (XOF) known as SHAKE functions. The details of the operation are described in the SHA3 specification, FIPS 202 known as sponge construction. It has been adapted from OpenTitan and you can find documentation describing the functionality of the KMAC block it was derived from here. In the current use cases of the SHA3 HW IP, either (a) messages are not considered secret (External Mu), or (b) SCA hardening would not be meaningful (HPKE in OCP L.O.C.K.), hence there are no SCA requirements.
Features
- Support for SHA3-224, 256, 384, 512, SHAKE[128, 256] and cSHAKE[128, 256]
- Support byte-granularity on input message
- Support arbitrary output length for SHAKE, cSHAKE
- Support customization input string S, and function-name N up to 36 bytes total
- 64b x 10 depth Message FIFO
- Performance (at 100 MHz):
- SHA3-224: 2.93 B/cycle, 2.34 Gbit/s - 1.19 B/cycle, 952 Mbit/s (DOM)
- SHA3-512: 1.47 B/cycle, 1.18 Gbit/s - 0.59 B/cycle, 472 Mbit/s (DOM)
Design Details
Keccak Round
A Keccak round implements the Keccak_f function described in the SHA3 specification.
Keccak round logic in SHA3 HWIP not only supports 1600 bit internal states but also all possible values {25, 50, 100, 200, 400, 800, 1600} based on a parameter Width.
Keccak permutations in the specification allow arbitrary number of rounds.
This module, however, supports Keccak_f which always runs 12 + 2*L rounds, where \[ L = log_2 {( {Width \over 25} )} \] .
For instance, 200 bits of internal state run 18 rounds.
SHA3 instantiates the Keccak round module with 1600 bit.
Keccak round logic has two phases inside. Theta, Rho, Pi functions are executed at the 1st phase. Chi and Iota functions run at the 2nd phase. The first phase and the second phase run in the same cycle.
To save circuit area, the Chi function uses 800 instead 1600 DOM multipliers but the multipliers are fully pipelined. The Chi and Iota functions are thus separately applied to the two halves of the state and the 2nd phase takes in total three clock cycles to complete. In the first clock cycle of the 2nd phase, the first stage of Chi is computed for the first lane halves of the state. In the second clock cycle, the new first lane halves are output and written to state register. At the same time, the first stage of Chi is computed for the second lane halves. In the third clock cycle, the new second lane halves are output and written to the state register.
Padding for Keccak
Padding logic supports SHA3/SHAKE/cSHAKE algorithms.
cSHAKE needs the extra inputs for the Function-name N and the Customization string S.
Other than that, SHA3, SHAKE, and cSHAKE share similar datapath inside the padding module except the last part added next to the end of the message.
SHA3 adds 2'b 10, SHAKE adds 4'b 1111, cSHAKE adds 2'b00 then pad10*1() follows.
All are little-endian values.
Interface between this padding logic and the MSG_FIFO follows the conventional FIFO interface.
So caliptra_prim_fifo_* can talk to the padding logic directly.
This module talks to Keccak round logic with a more memory-like interface.
The interface has an additional address signal on top of the valid, ready, and data signals.
The hashing process begins when the software issues the start command to CMD .
If cSHAKE is enabled, the padding logic expands the prefix value (N || S above) into a block size.
The block size is determined by the CFG_SHADOWED.kstrength.
If the value is 128, the block size will be 168 bytes.
If it is 256, the block size will be 136 bytes.
The expanded prefix value is transmitted to the Keccak round logic.
After sending the block size, the padding logic triggers the Keccak round logic to run a full 24 rounds.
If the mode is not cSHAKE, or cSHAKE mode and the prefix block has been processed, the padding logic accepts the incoming message bitstream and forward the data to the Keccak round logic in a block granularity. The padding logic controls the data flow and makes the Keccak logic to run after sending a block size.
After the software writes the message bitstream, it should issue the Process command into CMD register.
The padding logic, after receiving the Process command, appends proper ending bits with respect to the CFG_SHADOWED.mode value.
The logic writes 0 up to the block size to the Keccak round logic then ends with 1 at the end of the block.
After the Keccak round completes the last block, the padding logic asserts an absorbed signal to notify the software.
At this point, the software is able to read the digest in STATE memory region.
If the output length is greater than the Keccak block rate in SHAKE and cSHAKE mode, the software may run the Keccak round manually by issuing Run command to CMD register.
The software completes the operation by issuing Done command after reading the digest. The padding logic clears internal variables and goes back to Idle state.
Message FIFO
The SHA3 HWIP has a compile-time configurable depth message FIFO inside. The message FIFO receives incoming message bitstream regardless of its byte position in a word. Then it packs the partial message bytes into the internal 64 bit data width. After packing the data, the logic stores the data into the FIFO until the internal SHA3 engine consumes the data.
FIFO Depth calculation
The depth of the message FIFO is chosen to cover the throughput of the software or other producers such as DMA engine. The size of the message FIFO is enough to hold the incoming data while the SHA3 engine is processing the previous block. Default design parameters assume the system characteristics as below:
kmac_pkg::RegLatency: The register write takes 5 cycles.kmac_pkg::Sha3Latency: Keccak round latency takes 24 cycles.
Empty and Full status
Under normal operating conditions, the SHA3 engine will process data a lot faster than software can push it to the Message FIFO.
The Message FIFO depth observable from STATUS.fifo_depth will remain 0 while the STATUS.fifo_empty status bit is lowered for one clock cycle whenever software provides new data.
After the SHA3 engine starts popping the data again, the Message FIFO will eventually run empty again and the fifo_empty status interrupt will fire.
Note that the fifo_empty status interrupt will not fire if i) one of the hardware application interfaces is using the SHA3 block, ii) the SHA3 core is not in the Absorb state, or iii) after software has written the Process command.
If software pushes data to the Message FIFO while it is full, the write operation is blocked until there is again space in the FIFO.
This means the processor is effectively stalled.
If the SHA3 engine is currently running and software fills up the Message FIFO, the resulting stall won't take more than 100 clock cycles.
The stall mechanism prevents data loss and the upper bound on the wait time avoids software needing to poll the STATUS.fifo_depth field before writing data.
Programmer's guide
The software can update the SHA3 configurations only when the IP is in the idle state.
The software should check STATUS.sha3_idle before updating the configurations.
The software must first program CFG_SHADOWED.msg_endianness and CFG_SHADOWED.state_endianness at the initialization stage.
These determine the byte order of incoming messages (msg_endianness) and the Keccak state output (state_endianness).
Software Initiated SHA3 process
This section describes the expected software process to run the SHA3 HWIP.
At first, the software configures CFG_SHADOWED.kmac_en for the operation.
If SHA3 is enabled, the software should configure CFG_SHADOWED.mode to cSHAKE and CFG_SHADOWED.kstrength to 128 or 256 bit security strength.
The software also updates PREFIX registers if cSHAKE mode is used.
Current design does not convert cSHAKE mode to SHAKE even if PREFIX is empty string.
It is the software's responsibility to change the CFG_SHADOWED.mode to SHAKE in case of empty PREFIX.
The SHA3 HWIP uses PREFIX registers as it is.
It means that the software should update PREFIX with encoded values.
After configuring, the software notifies the SHA3 engine to accept incoming messages by issuing Start command into CMD.
If Start command is not issued, the incoming message is discarded.
After the software pushes all messages, it issues Process command to CMD for SHA3 engine to complete the sponge absorbing process.
SHA3 hashing engine pads the incoming message as defined in the SHA3 specification.
After the SHA3 engine completes the sponge absorbing step, it generates kmac_done interrupt.
Or the software can poll the STATUS.squeeze bit until it becomes 1.
In this stage, the software may run the Keccak round manually.
If the desired digest length is greater than the Keccak rate, the software issues Run command for the Keccak round logic to run one full round after the software reads the current available Keccak state.
At this stage, SHA3 does not raise an interrupt when the Keccak round completes the software initiated manual run.
The software should check STATUS.squeeze register field for the readiness of STATE value.
After the software reads all the digest values, it issues Done command to CMD register to clear the internal states.
Done command clears the Keccak state, FSM in SHA3, and a few internal variables.
Endianness
This SHA3 HWIP operates in little-endian. Internal SHA3 hashing engine receives in 64-bit granularity. The data written to SHA3 is assumed to be little endian.
The software may write/read the data in big-endian order if CFG_SHADOWED.msg_endianness or CFG_SHADOWED.state_endianness is set.
If the endianness bit is 1, the data is assumed to be big-endian.
So, the internal logic byte-swap the data.
For example, when the software writes 0xDEADBEEF with endianness as 1, the logic converts it to 0xEFBEADDE then writes into MSG_FIFO.
HMAC512/HMAC384
Hash-based message authentication code (HMAC) is a cryptographic authentication technique that uses a hash function and a secret key. HMAC involves a cryptographic hash function and a secret cryptographic key. This implementation supports the HMAC512 variants HMAC-SHA-512-256 and HMAC-SHA-384-192 as specified in NIST FIPS 198-1 [5]. The implementation is compatible with the HMAC-SHA-512-256 and HMAC-SHA-384-192 authentication and integrity functions defined in RFC 4868 [6].
Caliptra HMAC implementation uses SHA512 as the hash function, accepts a 512-bit key, and generates a 512-bit tag.
The implementation also supports PRF-HMAC-SHA-512. The PRF-HMAC-SHA-512 algorithm is identical to HMAC-SHA-512-256, except that variable-length keys are permitted, and the truncation step is not performed.
The HMAC algorithm is described as follows:
- The key is fed to the HMAC core to be padded
- The message is broken into 1024-bit chunks by the host
- For each chunk:
- The message is fed to the HMAC core
- The HMAC core should be triggered by the host
- The HMAC core status is changed to ready after hash processing
- The result digest can be read after feeding all message chunks
Operation
Padding
The message should be padded before feeding to the HMAC core. Internally, the i_padded key is concatenated with the message. The input message is taken, and some padding bits are appended to the message to get it to the desired length. The bits that are used for padding are simply β0β bits with a leading β1β (100000β¦000).
The total size should be equal to 128 bits, short of a multiple of 1024 because the goal is to have the formatted message size as a multiple of 1024 bits (N x 1024).
Figure: HMAC input formatting

The following figures show examples of input formatting for different message lengths.
Figure: Message length of 1023 bits

When the message is 1023 bits long, padding is given in the next block along with message size.
Figure: 1 bit padding

When the message size is 895 bits, a padding of β1β is also considered valid, followed by the message size.
Figure: Multi block message

Messages with a length greater than 1024 bits are broken down into N 1024-bit blocks. The last block contains padding and the size of the message.
Hashing
The HMAC512 core performs the sha2-512 function to process the hash value of the given message. The algorithm processes each block of the 1024 bits from the message, using the result from the previous block. This data flow is shown in the following figure.
Figure: HMAC-SHA-512-256 data flow

The HMAC384 core performs the sha2-384 function to process the hash value of the given message. The algorithm processes each block of the 1024 bits from the message, using the result from the previous block. This data flow is shown in the following figure.
Figure: HMAC-SHA-384-192 data flow

FSM
The HMAC architecture has the finite-state machine as shown in the following figure.
Figure: HMAC FSM

CSR Mode
When the CSR Mode register is set, the HMAC512 core uses the value latched from the cptra_csr_hmac_key interface pins in place of the API key register. These pins are latched internally after powergood assertion during DEVICE_MANUFACTURING lifecycle state. During debug mode operation this value is overridden with all 1's, and during any other lifecycle state it has a value of zero.
Signal descriptions
The HMAC architecture inputs and outputs are described in the following table.
| Name | Input or output | Description |
|---|---|---|
| clk | input | All signal timings are related to the rising edge of clk. |
| reset_n | input | The reset signal is active LOW and resets the core. This is the only active LOW signal. |
| init | input | The core is initialized and processes the key and the first block of the message. |
| next | input | The core processes the rest of the message blocks using the result from the previous blocks. |
| zeroize | input | The core clears all internal registers to avoid any SCA information leakage. |
| csr_mode | input | When set, the key comes from the cptra_csr_hmac_key interface pins. This key is valid only during MANUFACTURING mode. |
| mode | input | Indicates the hmac type of the function. This can be: - HMAC384 - HMAC512. |
| cptra_csr_hmac_key[511:0] | input | The key to be used during csr mode. |
| key[511:0] | input | The input key. |
| block[1023:0] | input | The input padded block of message. |
| LFSR_seed[383:0] | Input | The input to seed PRNG to enable the masking countermeasure for SCA protection. |
| ready | output | When HIGH, the signal indicates the core is ready. |
| tag[511:0] | output | The HMAC value of the given key or block. For PRF-HMAC-SHA-512, a 512-bit tag is required. For HMAC-SHA-512-256, the host is responsible for reading 256 bits from the MSB. |
| tag_valid | output | When HIGH, the signal indicates the result is ready. |
Address map
The HMAC address map is shown here: hmac_reg β clp Reference (chipsalliance.github.io).
Pseudocode
The following pseudocode demonstrates how the HMAC interface can be implemented.
Figure: HMAC pseudocode

SCA countermeasure
In an attack model, an attacker can form hypotheses about the secret key value and compute the corresponding output values by using the Hamming Distance model as an appropriate leakage model. An attacker who has knowledge of the implementation for open-source architecture has an advantage. The attacker can capture the power consumption traces to verify their hypotheses, by partitioning the acquisitions or using Pearsonβs correlation coefficient.
To protect the HMAC algorithm from side-channel attacks, a masking countermeasure is applied. This means that random values are added to the intermediate variables during the algorithmβs execution, so that the side-channel signals do not reveal any information about them.
The embedded countermeasures are based on "Differential Power Analysis of HMAC Based on SHA-2, and Countermeasures" by McEvoy et. al. To provide the required random values for masking intermediate values, a lightweight 74-bit LFSR is implemented. Based on βSpin Me Right Round Rotational Symmetry for FPGA-specific AESβ by Wegener et. al., LFSR is sufficient for masking statistical randomness.
Each round of SHA512 execution needs 6,432 random bits, while one HMAC operation needs at least 4 rounds of SHA512 operations. However, the proposed architecture requires only 384-bit LFSR seed and provides first-order DPA attack protection at the cost of 10% latency overhead with negligible hardware resource overhead.
Performance
The HMAC core performance is reported considering two different architectures: pure hardware architecture, and hardware/software architecture. These are described next.
Pure hardware architecture
In this architecture, the HMAC interface and controller are implemented in hardware. The performance specification of the HMAC architecture is reported as shown in the following table.
| Operation | Cycle count [CCs] | Time [us] @ 400 MHz | Throughput [op/s] |
|---|---|---|---|
| Data_In transmission | 44 | 0.11 | - |
| Process | 254 | 0.635 | - |
| Data_Out transmission | 12 | 0.03 | - |
| Single block | 310 | 0.775 | 1,290,322 |
| Double block | 513 | 1.282 | 780,031 |
| 1 KiB message | 1,731 | 4.327 | 231,107 |
| 128 KiB message | 207,979 | 519.947 | 1,923 |
Hardware/software architecture
In this architecture, the HMAC interface and controller are implemented in RISC-V core. The performance specification of the HMAC architecture is reported as shown in the following table.
| Operation | Cycle count [CCs] | Time [us] @ 400 MHz | Throughput [op/s] |
|---|---|---|---|
| Data_In transmission | 1389 | 3.473 | - |
| Process | 253 | 0.633 | - |
| Data_Out transmission | 290 | 0.725 | - |
| Single block | 1932 | 4.83 | 207,039 |
| Double block | 3166 | 7.915 | 136,342 |
| 1 KiB message | 10,570 | 26.425 | 37,842 |
| 128 KiB message | 1,264,314 | 3,160.785 | 316 |
HMAC_DRBG
Hash-based message authentication code (HMAC) deterministic random bit generator (DRBG) is a cryptographic random bit generator that uses a HMAC function. HMAC_DRBG involves a cryptographic HMAC function and a seed. This architecture is designed as specified in section 10.1.2. of NIST SP 800-90A [12]. For ECC signing operation, the implementation is compatible with section 3.1. of RFC 6979 [7].
Caliptra HMAC_DRBG implementation uses HMAC384 as the HMAC function, accepts a 384-bit seed, and generates a 384-bit random value.
The HMAC algorithm is described as follows:
- The seed is fed to HMAC_DRBG core by the host
- For each 384-bit random value
- The core should be triggered by the host
- The HMAC_DRBG core status is changed to ready after HMAC processing
- The result digest can be read
Operation
HMAC_DRBG uses a loop of HMAC(K, V) to generate the random bits. In this algorithm, two constant values of K_init and V_init are used as follows:
1. Set V_init = 0x01 0x01 0x01 ... 0x01 (V has 384-bit)
2. Set K_init = 0x00 0x00 0x00 ... 0x00 (K has 384-bit)
3. K_tmp = HMAC(K_init, V_init || 0x00 || entropy || nonce)
4. V_tmp = HMAC(K_tmp, V_init)
5. K_new = HMAC(K_tmp, V_tmp || 0x01 || entropy || nonce)
6. V_new = HMAC(K_new, V_tmp)
7. Set T = []
8. T = T || HMAC(K_new, V_new)
9. Return T if T is within the [1,q-1] range, otherwise:
10. K_new = HMAC(K_new, V_new || 0x00)
11. V_new = HMAC(K_new, V_new)
12. Jump to 8
For ECC KeyGen operation, HMAC_DRBG is used to generate privkey as follows:
Privkey = HMAC_DRBG(seed, nonce)
For ECC SIGNING operation, HMAC_DRBG is used to generate k as follows:
K = HMAC_DRBG(privkey, hashed_msg)
Signal descriptions
The HMAC_DRBG architecture inputs and outputs are described in the following table.
| Name | Input or output | Description |
|---|---|---|
| clk | input | All signal timings are related to the rising edge of clk. |
| reset_n | input | The reset signal is active LOW and resets the core. This is the only active LOW signal. |
| init | input | The core is initialized with the given seed and generates a 384-bit random value. |
| next | input | The core generates a new 384-bit random value using the result from the previous run. |
| zeroize | input | The core clears all internal registers to avoid any SCA information leakage. |
| entropy [383:0] | input | The input entropy. |
| nonce [383:0] | input | The input nonce. |
| LFSR_seed [147 :0] | input | The input to seed PRNG to enable masking countermeasure for SCA protection. |
| ready | output | When HIGH, the signal indicates the core is ready. |
| drbg [383:0] | output | The hmac_drbg value of the given inputs. |
| valid | output | When HIGH, the signal indicates the result is ready. |
Address map
The HMAC_DRBG is embedded into ECC architecture, since there is no address map to access it from FW.
SCA countermeasure
For information, see SCA countermeasure in the HMAC384 section.
ECC
The ECC unit includes the ECDSA (Elliptic Curve Digital Signature Algorithm) engine and the ECDH (Elliptic Curve Diffie-Hellman Key-Exchange) engine, offering a variant of the cryptographically secure Digital Signature Algorithm (DSA) and Diffie-Hellman Key-Exchange (DH), which uses elliptic curve (ECC). A digital signature is an authentication method in which a public key pair and a digital certificate are used as a signature to verify the identity of a recipient or sender of information.
The hardware implementation supports deterministic ECDSA, 384 Bits (Prime Field), also known as NIST-Secp384r1, described in RFC6979.
The hardware implementation also supports ECDH, 384 Bits (Prime Field), also known as NIST-Secp384r1, described in SP800-56A.
Secp384r1 parameters are shown in the following figure.
Figure: Secp384r1 parameters

Operation
The ECDSA consists of three operations, shown in the following figure.
Figure: ECDSA operations

The ECDH also consists of the sharedkey generation.
KeyGen
In the deterministic key generation, the paired key of (privKey, pubKey) is generated by KeyGen(seed, nonce), taking a deterministic seed and nonce. The KeyGen algorithm is as follows:
- Compute privKey = HMAC_DRBG(seed, nonce) to generate a random integer in the interval [1, n-1] where n is the group order of Secp384 curve.
- Generate pubKey(x,y) as a point on ECC calculated by pubKey=privKey Γ G, while G is the generator point over the curve.
Signing
In the signing algorithm, a signature (r, s) is generated by Sign(privKey, h), taking a privKey and hash of message m, h = hash(m), using a cryptographic hash function, SHA512. The signing algorithm includes:
- Generate a random number k in the range [1..n-1], while k = HMAC_DRBG(privKey, h)
- Calculate the random point R = k Γ G
- Take r = Rx mod n, where Rx is x coordinate of R=(Rx, Ry)
- Calculate the signature proof: s = k-1 Γ (h + r Γ privKey) mod n
- Return the signature (r, s), each in the range [1..n-1]
Verifying
The signature (r, s) can be verified by Verify(pubKey ,h ,r, s) considering the public key pubKey and hash of message m, h=hash(m) using the same cryptographic hash function SHA512. The output is rβ value of verifying a signature. The ECDSA verify algorithm includes:
- Calculate s1 = sβ1 mod n
- Compute R' = (h Γ s1) Γ G + (r Γ s1) Γ pubKey
- Take rβ = R'x mod n, while R'x is x coordinate of Rβ=(R'x, R'y)
- Verify the signature by comparing whether r' == r
ECDH sharedkey
In ECDH sharedkey generation, the shared key is generated by ECDH_sharedkey(privKey_A, pubKey_B), taking an own prikey and other party pubkey. The ECDH sharedkey algorithm is as follows:
- Compute P = sharedkey(privkey_A, pubkey_b) where P(x,y) is a point on ECC.
- Output sharedkey = Px, where Px is x coordinate of P.
Architecture
The ECC top-level architecture is shown in the following figure.
Figure: ECC architecture

Signal descriptions
The ECC architecture inputs and outputs are described in the following table.
| Name | Input or output | Description |
|---|---|---|
| clk | input | All signal timings are related to the rising edge of clk. |
| reset_n | input | The reset signal is active LOW and resets the core. This is the only active LOW signal. |
| ctrl[1:0] | input | Indicates the AES type of the function. This can be: β 0b00: No Operation β 0b01: KeyGen β 0b10: Signing β 0b11: Verifying |
| zeroize | input | The core clears all internal registers to avoid any SCA information leakage. |
| seed [383:0] | input | The deterministic seed for HMAC_DRBG in the KeyGen operation. |
| nonce [383:0] | input | The deterministic nonce for HMAC_DRBG in the KeyGen operation. |
| privKey_in[383:0] | input | The input private key used in the signing operation. |
| pubKey_in[1:0][383:0] | input | The input public key(x,y) used in the verifying operation. |
| hashed_msg[383:0] | input | The hash of message using SHA512. |
| ready | output | When HIGH, the signal indicates the core is ready. |
| privKey_out[383:0] | output | The generated private key in the KeyGen operation. |
| pubKey_out[1:0][383:0] | output | The generated public key(x,y) in the KeyGen operation. |
| r[383:0] | output | The signature value of the given priveKey/message. |
| s[383:0] | output | The signature value of the given priveKey/message. |
| rβ[383:0] | Output | The signature verification result. |
| DH_sharedkey[383:0] | output | The generated shared key in the ECDH sharedkey operation. |
| valid | output | When HIGH, the signal indicates the result is ready. |
Address map
The ECC address map is shown here: ecc_reg β clp Reference (chipsalliance.github.io).
Pseudocode
The following pseudocode blocks demonstrate example implementations for KeyGen, Signing, Verifying, and ECDH sharedkey.
KeyGen
Figure: KeyGen pseudocode

Signing
Figure: Signing pseudocode

Verifying
Figure: Verifying pseudocode

ECDH sharedkey
Figure: ECDH sharedkey pseudocode

SCA countermeasure
The described ECC has four main routines: KeyGen, Signing, Verifying, and ECDH sharedkey. Since the Verifying routine requires operation with public values rather than a secret value, our side-channel analysis does not cover this routine. Our evaluation covers the KeyGen, Signing, and ECDH sharedkey routines where the secret values are processed.
KeyGen consists of HMAC DRBG and scalar multiplication, while Signing first requires a message hashing and then follows the same operations as KeyGen (HMAC DRBG and scalar multiplication). The last step of Signing is generating βSβ as the proof of signature. Since HMAC DRBG and hash operations are evaluated separately in our document, this evaluation covers scalar multiplication and modular arithmetic operations.
Scalar multiplication
To perform the scalar multiplication, the Montgomery ladder is implemented, which is inherently resistant to timing and single power analysis (SPA) attacks.
Implementation of complete unified addition formula for the scalar multiplication avoids information leakage and enhances architecture from security and mathematical perspectives.
To protect the architecture against horizontal power/electromagnetic (EM) and differential power analysis (DPA) attacks, several countermeasures are embedded in the design [9]. Since these countermeasures require random inputs, HMAC-DRBG is fed by IV to generate these random values.
Since HMAC-DRBG generates random value in a deterministic way, firmware MUST feed different IV to ECC engine for EACH keygen, signing, and ECDH sharedkey operation.
Base point randomization
This countermeasure is achieved using the randomized base point in projective coordinates. Hence, the base point G=(Gx, Gy) in affine coordinates is transformed and randomized to projective coordinates as (X, Y, Z) using a random value as follows:
This approach does not have the performance or area overhead because the architecture is variable-base-point implemented.
Scalar blinding
This countermeasure is achieved by randomizing the scalar as follows:
Based on [10], half of the bit size of is required to prevent advanced DPA attacks. Therefore, has 192 bits, and the blinded scalar has 576 bits. Hence, this countermeasure extends the Montgomery ladder iterations due to extended scalar.
This approach is achieved at the cost of 50% more latency on scalar multiplication and adding one lightweight block, including one 32*32 multiplier and an accumulator.
Note: the length of rand is configurable to have a trade-off between the required protection and performance.
Making countermeasures embedded into HMAC_DRBG
In the first step of the KeyGen operation, the privkey is generated using HMAC_DRBG by feeding the following two inputs: seed and nonce. To avoid SCA information leakage during this operation, we embed masking countermeasures into the HMAC_DRBG architecture.
Each round of SHA512 execution needs 6,432 random bits, and one HMAC operation needs at least 4 rounds of SHA512 operations. Furthermore, each HMAC_DRBG round needs at least 5 rounds of HMAC operations. However, the proposed architecture uses a lightweight LFSR and provides first-order DPA attack protection with negligible latency and hardware resource overhead.
ECDSA signing nonce leakage
Generating βSβ as the proof of signature at the steps of the signing operation leaks where the hashed message is signed with private key and ephemeral key as follows:
Since the given message is known or the signature part r is known, the attacker can perform a known-plaintext attack. The attacker can sign multiple messages with the same key, or the attacker can observe part of the signature that is generated with multiple messages but the same key.
The evaluation shows that the CPA attack can be performed with a small number of traces, respectively. Thus, an arithmetic masked design for these operations is implemented.
Masking signature
This countermeasure is achieved by randomizing the privkey as follows:
Although computation of βSβ seems the most vulnerable point in our scheme, the operation does not have a big contribution to overall latency. Hence, masking these operations has low overhead on the cost of the design.
Random number generator for SCA countermeasure
The ECC countermeasure requires several random vectors to randomize the intermediate values, as described in the preceding section. HMAC_DRBG is used to take one random vector of 384-bit (i.e., ECC_IV register) and to generate the required random vectors for different countermeasures.
The state machine of HMAC_DRBG utilization is shown in the following figure, including three main states:
- SCA random generator: Running HMAC_DRBG with IV and an internal counter to generate the required random vectors.
- KEYGEN PRIVKEY: Running HMAC_DRBG with seed and nonce to generate the privkey in KEYGEN operation.
- SIGNING NONCE: Running HMAC_DRBG based on RFC6979 in SIGNING operation with privkey and hashed_msg.
Figure: HMAC_DRBG utilization

In SCA random generator state:
- This state (in KeyGen operation mode) generates 3 384-bit random vectors for the following: LFSR, base point randomization, and scalar blinding randomization.
- This state (in signing operation) generates 4 384-bit random vectors for the following: LFSR, base point randomization, scalar blinding, and masking signature randomization.
- HMAC_DRBG is initialized with IV and an internal counter. This 64-bit counter enables after reset and zeroization and contains different values depending on when ECC is called.
- HMAC_DRBG is enabled by the INIT command. To generate all required vectors, HMAC-DRBG is continued by the NEXT command that increments the built-in counter inside the HMAC-DRBG unit.
- To initialize the required seed for LFSR, LFSR_INIT_SEED is set as a constant by RTL after reset and zeroization. However, this value is updated before enabling HMAC_DRBG as follows:
- In the first execution of HMAC_DRBG after reset and zeroization, hmac_lfsr_seed is equal to LFSR_INIT_SEED XORed by internal 64-bit counter.
- In the next executions of HMAC_DRBG, hmac_lfsr_seed is equal to HMAC_DRBG output of the first execution XORed by internal 64-bit counter.
The data flow of the HMAC_DRBG operation in keygen operation mode is shown in the following figure.
Figure: HMAC_DRBG data flow

TVLA results
Test vector leakage assessment (TVLA) provides a robust test using a π‘-test. This test evaluates the differences between sets of acquisitions to determine if one set of measurement can be distinguished from the other. This technique can detect different types of leakages, providing a clear indication of leakage or lack thereof.
In practice, observing a t-value greater than a specific threshold (mainly 4.5) indicates the presence of leakage. However, in ECC, due to its latency, around 5 million samples are required to be captured. This latency leads to many false positives and the TVLA threshold can be considered a higher value than 4.5. Based on the following figure from βSide-Channel Analysis and Countermeasure Design for Implementation of Curve448 on Cortex-M4β by Bisheh-Niasar et. al., the threshold can be considered equal to 7 in our case.
Figure: TVLA threshold as a function of the number of samples per trace

KeyGen TVLA
The TVLA results for performing seed/nonce-dependent leakage detection using 200,000 traces is shown in the following figure. Based on this figure, there is no leakage in ECC keygen by changing the seed/nonce after 200,000 operations.
Figure: seed/nonce-dependent leakage detection using TVLA for ECC keygen after 200,000 traces

Signing TVLA
The TVLA results for performing privkey-dependent leakage detection using 20,000 traces is shown in the following figure. Based on this figure, there is no leakage in ECC signing by changing the privkey after 20,000 operations.
Figure: privkey-dependent leakage detection using TVLA for ECC signing after 20,000 traces

The TVLA results for performing message-dependent leakage detection using 64,000 traces is shown in the following figure. Based on this figure, there is no leakage in ECC signing by changing the message after 64,000 operations.
Figure: Message-dependent leakage detection using TVLA for ECC signing after 64,000 traces

The point with t-value equal to -40 is mapped to the Montgomery conversion of the message that is a publicly known value (no secret is there). By ignoring those corresponding samples, there are some sparse samples with a t-value greater than 7, as shown in the following table.
| Sample | Duration | Cycle | t-value | Operation |
|---|---|---|---|---|
| 4,746,127 | 214 | 911,381.4 | 11.2 | start of mont_conv(msg) |
| 4,746,341 | 911,422.5 | -40 | end of mont_conv(msg) | |
| 4,757,797 | 1 | 913,622.0 | 7.4 | inv_q |
| 4,768,302 | 1 | 915,639.0 | 7.8 | inv_q |
| 4,779,610 | 1 | 917,810.1 | -9.1 | inv_q |
| 4,788,120 | 1 | 919,444.0 | 7.6 | inv_q |
| 4,813,995 | 1 | 924,412.0 | 7.3 | inv_q |
| 4,822,693 | 1 | 926,082.1 | 7.5 | inv_q |
| 4,858,671 | to the end | 932,989.8 | -7.6 | Ended ECC signing |
Performance
The ECC core performance is reported in the next section.
Pure hardware architecture
In this architecture, the ECC interface and controller are implemented in hardware. The performance specification of the ECC architecture is reported as shown in the following table.
| Operation | Cycle count [CCs] | Time [ms] @ 400 MHz | Throughput [op/s] |
|---|---|---|---|
| Keygen | 909,648 | 2.274 | 439 |
| Signing | 932,990 | 2.332 | 428 |
| Verifying | 1,223,938 | 3.060 | 326 |
LMS Accelerator
LMS cryptography is a type of hash-based digital signature scheme that was standardized by NIST in 2020. It is based on the Leighton-Micali Signature (LMS) system, which uses a Merkle tree structure to combine many one-time signature (OTS) keys into a single public key. LMS cryptography is resistant to quantum attacks and can achieve a high level of security without relying on large integer mathematics.
Caliptra supports only LMS verification using a software/hardware co-design approach. Hence, the LMS accelerator reuses the SHA256 engine to speedup the Winternitz chain by removing software-hardware interface overhead. The LMS-OTS verification algorithm is shown in following figure:
Figure: LMS-OTS Verification algorithm

The high-level architecture of LMS is shown in the following figure.
Figure: LMS high-level architecture

LMS parameters
LMS parameters are shown in the following table:
| Parameter | Description | Value |
|---|---|---|
| n | The number of bytes of the output of the hash function. | {24, 32} |
| w | The width (in bits) of the Winternitz coefficients. | {1, 2, 4, 8} |
| p | The number of n-byte string elements that make up the LM-OTS signature. | {265, 133, 67, 34} |
| H | A cryptographic hash function. | SHA256 |
| h | The height of the tree. | {5, 10, 15, 20, 25} |
- SHA256 is used for n=32 and SHA256/192 is used for n=24.
- SHAKE256 is not supported in this architecture.
- Value of p is determined based on w. If w=1, p is equal to 265, and so on.
Winternitz Chain Accelerator
The Winternitz hash chain can be accelerated in hardware to enhance the performance of the design. For that, a configurable architecture is proposed that can reuse SHA256 engine. The LMS accelerator architecture is shown in the following figure, while H is SHA256 engine.
Figure: Winternitz chain architecture

Signal descriptions
The LMS accelerator integrated into SHA256 architecture inputs and outputs are described as follows.
| Name | Input or output | Description |
|---|---|---|
| clk | input | All signal timings are related to the rising edge of clk. |
| reset_n | input | The reset signal is active LOW and resets the core. This is the only active LOW signal. |
| init | input | The core is initialized and processes the first block of message. |
| next | input | The core processes the rest of the message blocks using the result from the previous blocks. |
| mode | input | Indicates the hash type of the function. This can be: - SHA256/224 - SHA256 |
| zeroize | input | The core clears all internal registers to avoid any SCA information leakage. |
| WNTZ_MODE | input | SHA256 core is configured in Winternitz verification mode. |
| WNTZ_W[3:0] | input | Winternitz W value. |
| WNTZ_N_MODE | input | Winternitz n value(SHA192/SHA256 --> n = 24/32) |
| block[511:0] | input | The input padded block of message. |
| ready | output | When HIGH, the signal indicates the core is ready. |
| digest[255:0] | output | The hashed value of the given block. |
| digest_valid | output | When HIGH, the signal indicates the result is ready. |
Address map
The address map for LMS accelerator integrated into SHA256 is shown here: sha256_reg β clp Reference (chipsalliance.github.io).
Adams Bridge - Dilithium (ML-DSA)
Please refer to the Adams-bridge specification
Address map
Address map of ML-DSA accelerator is shown here: ML-DSA_reg β clp Reference (chipsalliance.github.io)
Adams Bridge Kyber ML-KEM
Please refer to the Adams-bridge specification
Address map
Address map of ML-KEM accelerator is shown here: ML-KEM_reg β clp Reference (chipsalliance.github.io)
AES
The AES unit is a cryptographic accelerator that processes requests from the processor to encrypt or decrypt 16-byte data blocks. It supports AES-128/192/256 in various modes, including Electronic Codebook (ECB), Cipher Block Chaining (CBC), Cipher Feedback (CFB) with a fixed segment size of 128 bits (CFB-128), Output Feedback (OFB), Counter (CTR), and Galois/Counter Mode (GCM).
The AES unit is reused from here, (see aes with a shim to translate from AHB-lite to the tl-ul interface.
Additional registers have been added to support key vault integration. Keys from the key vault can be loaded into the AES unit to be used for encryption or decryption.
Operation
For more information, see the AES Programmer's Guide.
AES Endian
The AES Core uses little endian for the DATA_IN and DATA_OUT registers. Caliptra allows a user to stream the data into and out of AES in big endian when AES_CLP.CTRL0.ENDIAN_SWAP is set to 1. This is done by swizzling the write and read data when a write targets DATA_IN or a read targets DATA_OUT.
By default little endian is selected.
Signal descriptions
The AES architecture inputs and outputs are described in the following table.
| Name | Input or output | Description |
|---|---|---|
| clk | input | All signal timings are related to the rising edge of clk. |
| reset_n | input | The reset signal is active LOW and resets the core. This is the only active LOW signal. |
| DATA_IN | input | Input block to be encrypted or decrypted. Written in four 32-bit registers. |
| DATA_OUT | output | Output block result of encryption or decryption. Stored in four 32-bit registers. |
| CTRL_SHADOWED.MANUAL_OPERATION | input | Configures the AES core to operation in manual mode. |
| CTRL_SHADOWED.PRNG_RESEED_RATE | input | Configures the rate of reseeding the internal PRNG used for masking. |
| CTRL_SHADOWED.SIDELOAD | input | When asserted, AES core will use the key from the keyvault interface. |
| CTRL_SHADOWED.KEY_LEN | input | Configures the AES key length. Supports 128, 192, and 256-bit keys. |
| CTRL_SHADOWED.MODE | input | Configures the AES block cipher mode. |
| CTRL_SHADOWED.OPERATION | input | Configures the AES core to operate in encryption or decryption modes. |
| CTRL_GCM_SHADOWED.PHASE | input | Configures the GCM phase. |
| CTRL_GCM_SHADOWED.NUM_VALID_BYTES | input | Configures the number of valid bytes of the current input block in GCM. |
| TRIGGER.PRNG_RESEED | input | Forces a PRNG reseed. |
| TRIGGER.DATA_OUT_CLEAR | input | Clears the DATA_OUT registers with pseudo-random data. |
| TRIGGER.KEY_IV_DATA_IN_CLEAR | input | Clears the Key, IV, and DATA_INT registers with pseudo-random data. |
| TRIGGER.START | input | Triggers the encryption/decryption of one data block if in manual operation mode. |
| STATUS.ALERT_FATAL_FAULT | output | A fatal fault has ocurred and the AES unit needs to be reset. |
| STATUS.ALERT_RECOV_CTRL_UPDATE_ERR | output | An update error has occurred in the shadowed Control Register. AES operation needs to be restarted by re-writing the Control Register. |
| STATUS.INPUT_READY | output | The AES unit is ready to receive new data input via the DATA_IN registers. |
| STATUS.OUTPUT_VALID | output | The AES unit has alid output data. |
| STATUS.OUTPUT_LOST | output | All previous output data has been fully read by the processor (0) or at least one previous output data block has been lost (1). It has been overwritten by the AES unit before the processor could fully read it. Once set to 1, this flag remains set until AES operation is restarted by re-writing the Control Register. The primary use of this flag is for design verification. This flag is not meaningful if MANUAL_OPERATION=0. |
| STATUS.STALL | output | The AES unit is stalled because there is previous output data that must be read by the processor before the AES unit can overwrite this data. This flag is not meaningful if MANUAL_OPERATION=1. |
| STATUS.IDLE | output | The AES unit is idle. |
Address map
The AES address map is shown here: aes_clp_reg β clp Reference (chipsalliance.github.io).
SCA countermeasures
The AES unit employs separate SCA countermeasures for the AES cipher core used for the encryption/decryption part and for the GHASH module used for computing the integrity tag in GCM.
AES cipher core
A detailed specification of the SCA countermeasure employed in the AES cipher core is shown here: AES cipher core SCA countermeasure. The most critical building block of the SCA countermeasure, i.e., the masked AES S-Box, successfully passes formal masking verification at the netlist level using Alma: Execution-aware Masking Verification. The flow required for repeating the formal masking verification using Alma together with a Howto can be found here. The entire AES cipher core including the masked S-Boxes and as well as the PRNG generating the randomness for remasking successfully passes masking evaluation at the netlist level using PROLEAD - A Probing-Based Leakage Detection Tool for Hardware and Software. The flow required for repeating the masking evaluation using PROLEAD together with a Howto can be found here.
GHASH module
A detailed specification of the SCA countermeasure employed in the GHASH module is shown here: GHASH module SCA countermeasure.
To optimize and verify this masking countermeasure, two different types of experiments have been performed for which the results are given below.
- Formal masking verification using Alma: Execution-aware Masking Verification. These experiments led to a series of small design optimizations which have been integrated into Caliptra. The resulting design successfully passes formal masking verification at the netlist level.
- Test-vector leakage assessment (TVLA) applied to power SCA traces captured on a ChipWhisperer-based FPGA setup. These experiments confirm the formal masking verification results: No 1st-order SCA can be observed during the GHASH operation. The leakage observed at the boundary of and outside the GHASH operation can be attributed to the evaluation methodology and the handling of unmasked and uncritical data, as well as to FPGA-specific leakage effects known from literature. We are confident that the optimized SCA hardening concept effectively deters SCA attacks.
Formal masking verification using Alma
Alma is an open source, formal masking verification tool developed at TU Graz which enables formal verification of masking SCA countermeasures at the netlist level. The main advantages of this approach compared to analyzing FPGA power traces are as follows:
- The turn-around time is much faster as it does not involve FPGA bitstream generation and capturing power traces (both can take several hours).
- Netlist-based analysis tools typically enable pinpointing sources of SCA leakage and easily allow analyzing sub parts of the masked design individually. As a result, individual issues can be fixed up faster.
- The analyzed netlist is closer to the targeted ASIC implementation. During FPGA synthesis, the netlist is mapped to the logic elements such as look-up tables (LUTs) available on the selected FPGA which are fundamentally different from more simple ASIC gates.
However, formal netlist analysis tools may not be perfect and they also have limitations in terms of what can be analyzed. For example, the maximum supported netlist size depends on the complexity and number of the non-linear elements. Also, random number generators and in particular pseudo-random number generators typically need to be excluded from the analysis and random number inputs need to be assumed as ideal by tools. Thus, they donβt replace FPGA-based analysis. We use them to increase our confidence in our SCA countermeasures and to close countermeasure verification faster by reducing the number of FPGA evaluation runs.
Prerequisites
The Alma-based formal masking verification flow together with a Howto (including installation instructions) as well an open source Yosys synthesis flow are available open soure. The tool can both run on generic Yosys netlists or on proprietary and technology-specific netlists. For the latter, a slightly modified verification flow with an additional translation step is required. To verify the GHASH SCA countermeasure, the generic flow was used with the following tool versions:
- Alma (specific commit)
- Yosys 0.36 (git sha1 8f07a0d84)
- sv2v v0.0.11-28-g81d8225
- Verilator 4.214 2021-10-17 rev v4.214
Yosys Netlist Synthesis
Setup the open source Yosys synthesis flow by copying the syn_setup.example.sh file and renaming it to syn_setup.sh.
Change the LR_SYNTH_TOP_MODULE variable to aes_ghash_wrap and the LR_SYNTH_CELL_LIBRARY_PATH to the NangateOpenCellLibrary_typical.lib file in the folder where you installed the nangate45 library.
Then, start the synthesis by executing
./syn_yosys.sh
This should produce output similar to what is shown below:
8. Printing statistics.
=== aes_ghash_wrap ===
Number of wires: 24543
Number of wire bits: 29339
Number of public wires: 567
Number of public wire bits: 5363
Number of memories: 0
Number of memory bits: 0
Number of processes: 0
Number of cells: 26214
AND2_X1 1585
AND3_X1 4
AND4_X1 32
AOI211_X1 58
AOI21_X1 293
AOI221_X1 215
AOI22_X1 364
DFFR_X1 1468
DFFS_X1 5
INV_X1 584
MUX2_X1 1252
NAND2_X1 1870
NAND3_X1 128
NAND4_X1 37
NOR2_X1 7551
NOR3_X1 445
NOR4_X1 28
OAI211_X1 98
OAI21_X1 827
OAI221_X1 3
OAI22_X1 183
OR2_X1 28
OR3_X1 67
OR4_X1 2
XNOR2_X1 7122
XOR2_X1 1965
Chip area for module '\aes_ghash_wrap': 37534.728000
====== End Yosys Stat Report ======
Warnings: 20 unique messages, 102 total
End of script. Logfile hash: 16c4d13569, CPU: user 25.11s system 0.12s, MEM: 176.29 MB peak
Yosys 0.36 (git sha1 8f07a0d84, gcc 11.4.0-1ubuntu1~22.04 -fPIC -Os)
Time spent: 66% 2x abc (47 sec), 9% 40x opt_expr (6 sec), ...
Area in kGE = 47.04
Note that the reported area is quite a bit bigger compared to the number reported in the GHASH SCA countermeasure specification The reasons are twofold:
- The
aes_ghash_wrapmodule synthesized is a wrapper module around the GHASH module in focus of this analysis. The goal of the wrapper is to separately feed in secrets (the hash subkey H and the encrypted initial counter block S) as well as randomness in a tool aware manner. As such, the wrapper includes some additional muxing resources and a counter to ease interpretation of results. - To speed up the formal analysis, the pipelined Galois-field multipliers have been instantiated with a latency of 4 instead of 32 clock cycles as on FPGA. While the latency or more precisely the processing parallelism does have an impact on the SNR, it does not have an impact on the formal netlist analysis which is performed in a so-to-say noise free environment.
Formal Netlist Analysis
After synthesizing the netlist, the following steps should be taken to perform the analysis:
-
Make sure to source the
build_consts.shscriptsource util/build_consts.shin order to set up some shell variables.
-
Enter the directory where you have downloaded Alma and load the virtual Python environment
source dev/bin/activate -
Launch the Alma tool to parse, trace (simulate) and formally verify the netlist. For simplicity, a single script is provided to launch all the required steps with a single command. Simply run
${REPO_TOP}/hw/ip/aes/pre_sca/alma/verify_aes_ghash.shThis should produce output similar to the one below:
Verifying aes_ghash_wrap using Alma Starting yosys synthesis... | CircuitGraph | Total: 29882 | Linear: 9091 | Non-linear: 12741 | Registers: 1473 | Mux: 3538 | parse.py successful (47.99s) 1: Running verilator on given netlist 2: Compiling verilated netlist library 3: Compiling provided verilator testbench 4: Simulating circuit and generating VCD | CircuitGraph | Total: 29882 | Linear: 9091 | Non-linear: 12741 | Registers: 1473 | Mux: 3538 | tmp/tmp.vcd:24765: [WARNING] Entry for name alert_fatal_i already exists in namemap (alert_fatal_i -> Ce") tmp/tmp.vcd:24766: [WARNING] Entry for name alert_o already exists in namemap (alert_o -> De") tmp/tmp.vcd:24767: [WARNING] Entry for name clear_i already exists in namemap (clear_i -> Ee") tmp/tmp.vcd:24768: [WARNING] Entry for name clk_i already exists in namemap (clk_i -> Fe") tmp/tmp.vcd:24770: [WARNING] Entry for name cyc_ctr_o already exists in namemap (cyc_ctr_o -> Ge") tmp/tmp.vcd:24771: [WARNING] Entry for name data_in_prev_i already exists in namemap (data_in_prev_i -> He") tmp/tmp.vcd:24772: [WARNING] Entry for name data_out_i already exists in namemap (data_out_i -> Le") tmp/tmp.vcd:24773: [WARNING] Entry for name first_block_o already exists in namemap (first_block_o -> Pe") tmp/tmp.vcd:24774: [WARNING] Entry for name gcm_phase_i already exists in namemap (gcm_phase_i -> Qe") tmp/tmp.vcd:24775: [WARNING] Entry for name ghash_state_done_o already exists in namemap (ghash_state_done_o -> Re") tmp/tmp.vcd:24776: [WARNING] Entry for name hash_subkey_i already exists in namemap (hash_subkey_i -> Ve") tmp/tmp.vcd:24777: [WARNING] Entry for name in_ready_o already exists in namemap (in_ready_o -> ^e") tmp/tmp.vcd:24778: [WARNING] Entry for name in_valid_i already exists in namemap (in_valid_i -> _e") tmp/tmp.vcd:24779: [WARNING] Entry for name load_hash_subkey_i already exists in namemap (load_hash_subkey_i -> `e") tmp/tmp.vcd:24780: [WARNING] Entry for name num_valid_bytes_i already exists in namemap (num_valid_bytes_i -> ae") tmp/tmp.vcd:24781: [WARNING] Entry for name op_i already exists in namemap (op_i -> be") tmp/tmp.vcd:24782: [WARNING] Entry for name out_ready_i already exists in namemap (out_ready_i -> ce") tmp/tmp.vcd:24783: [WARNING] Entry for name out_valid_o already exists in namemap (out_valid_o -> de") tmp/tmp.vcd:24784: [WARNING] Entry for name prd_i already exists in namemap (prd_i -> ee") tmp/tmp.vcd:24785: [WARNING] Entry for name rst_ni already exists in namemap (rst_ni -> me") tmp/tmp.vcd:24786: [WARNING] Entry for name s_i already exists in namemap (s_i -> ne") 0 0 Building formula for cycle 0: vars 0 clauses 0 Checking cycle 0: Building formula for cycle 1: vars 1024 clauses 1536 Checking cycle 1: Building formula for cycle 2: vars 3968 clauses 6528 Checking cycle 2: Building formula for cycle 3: vars 6298 clauses 11026 Checking cycle 3: Building formula for cycle 4: vars 14888 clauses 34886 Checking cycle 4: Building formula for cycle 5: vars 20924 clauses 52734 Checking cycle 5: Building formula for cycle 6: vars 53986 clauses 143674 Checking cycle 6: Building formula for cycle 7: vars 57570 clauses 150970 Checking cycle 7: Building formula for cycle 8: vars 80484 clauses 169282 Checking cycle 8: Building formula for cycle 9: vars 213770 clauses 504198 Checking cycle 9: Building formula for cycle 10: vars 594390 clauses 1617276 Checking cycle 10: Building formula for cycle 11: vars 1024018 clauses 2881744 Checking cycle 11: Building formula for cycle 12: vars 1704424 clauses 4910342 Checking cycle 12: Building formula for cycle 13: vars 1713897 clauses 4915466 Checking cycle 13: Building formula for cycle 14: vars 1834911 clauses 5233038 Checking cycle 14: Building formula for cycle 15: vars 2258841 clauses 6492446 Checking cycle 15: Building formula for cycle 16: vars 2734646 clauses 7907830 Checking cycle 16: Building formula for cycle 17: vars 5868600 clauses 18374416 Checking cycle 17: Building formula for cycle 18: vars 5922747 clauses 18524578 Checking cycle 18: Building formula for cycle 19: vars 6100898 clauses 19061808 Checking cycle 19: Building formula for cycle 20: vars 6427297 clauses 20074334 Checking cycle 20: Building formula for cycle 21: vars 6949506 clauses 21693947 Checking cycle 21: Building formula for cycle 22: vars 6949506 clauses 21693947 Checking cycle 22: Building formula for cycle 23: vars 6949506 clauses 21693947 Checking cycle 23: Building formula for cycle 24: vars 7057992 clauses 21994175 Checking cycle 24: Building formula for cycle 25: vars 7407412 clauses 23047989 Checking cycle 25: Building formula for cycle 26: vars 7797810 clauses 24221073 Checking cycle 26: Building formula for cycle 27: vars 10939700 clauses 34732235 Checking cycle 27: Building formula for cycle 28: vars 11268148 clauses 35780811 Checking cycle 28: Building formula for cycle 29: vars 11268148 clauses 35780811 Checking cycle 29: Building formula for cycle 30: vars 11268148 clauses 35780811 Checking cycle 30: Building formula for cycle 31: vars 11376634 clauses 36081039 Checking cycle 31: Building formula for cycle 32: vars 11726054 clauses 37134853 Checking cycle 32: Building formula for cycle 33: vars 12116452 clauses 38307937 Checking cycle 33: Building formula for cycle 34: vars 15258342 clauses 48819099 Checking cycle 34: Building formula for cycle 35: vars 15586534 clauses 49867675 Checking cycle 35: Building formula for cycle 36: vars 15619430 clauses 49965979 Checking cycle 36: Finished in 3948.52 The execution is secure
Notes:
-
This analysis exercises the full data path of the GHASH block and comprises the following operations (controlled by a small Verilator testbench):
- Initial clearing of all internal registers.
- Loading the hash subkey H.
- Loading the encrypted initial counter block S including the subsequent generation of repeatedly used correction terms.
- Processing a first AAD/ciphertext block including the generation of a correction term that is used for the first block only.
- Processing a second AAD/ciphertext block.
- Producing the final authentication tag.
-
The following main changes have been implemented as a result of the formal netlist analysis using Alma (refer to the countermeasure spec for details):
- The result of the final addition of Share 1 of S and the unmasked GHASH state is no longer stored into the GHASH state register but directly forwarded to the output, and the state input to this addition is blanked.
The input multiplexer (
ghash_in_mux) loses one input. - The two 3-input multiplexers selecting the operands for the addition with the GHASH state (
add_in_mux) are replaced by one-hot multiplexers with registered control signals. - The Operand B inputs of both GF multipliers are now blanked. The 3-input multiplexer selecting Operand B of the second GF multiplier is replaced by a one-hot multiplexer with registered control signal. In addition, the last input slice of Operand B for this multiplier is registered. This allows the switching the multiplexer during the last clock cycle of the multiplication to avoid some undesirable transient leakage occurring upon saving the result of the multiplication into the GHASH state register (and this new value propagating through the multiplexer into the multiplier again).
- The GF multipliers are configured to output zero instead of Operand A (the hash subkey) while busy.
- The state input for the addition required for the generation of the correction term for Share 0 is blanked.
- Between adding the correction terms to the GHASH state for the last time and between unmasking the GHASH state, a bubble cycle is added to allow signals to fully settle thereby avoiding undesirable transient effects unmasking the uncorrected state shares.
- The result of the final addition of Share 1 of S and the unmasked GHASH state is no longer stored into the GHASH state register but directly forwarded to the output, and the state input to this addition is blanked.
The input multiplexer (
-
The overall area impact of these changes is low (+0.16 kGE in Yosys + nangate45).
-
The final design successfully passes the formal masking verification. For details regarding tool parameters, check the analysis script.
ChipWhisperer-based FPGA evaluation and TVLA
To underpin the results of the formal verification flow, the hardening of the GHASH module has been analyzed on the ChipWhisperer CW310 FPGA board. For this analysis, power traces with the ChipWhisperer Husky scope were captured during GCM operations. Afterwards a Test Vector Leakage Assessment (TVLA) with the ot-sca toolset has been performed. The setup is illustrated in the following Figure.
:--:
Figure: Target CW310 FPGA board (left) and the CW Husky scope (right).
Setup
:--:
Figure: Measurement setup. The main components are the target board, the scope, and the SCA framework.
The prior Figure gives a detailed overview of the measurement setup that has been utilized to capture the power traces. The SCA evaluation framework ot-sca is the central component of the measurement setup. It is responsible for communicating with the penetration testing framework that runs on the target FPGA board and with the scope. Initially, ot-sca configures the scope (sample rate, number of samples) and the pentest framework (which input, how many encryptions, where to trigger).
Based on the configuration, the pentest framework generates the cipher input, starts the encryption, and sends back the computed tag to ot-sca. The trigger is automatically set and unset by the AES hardware block to achieve an accurate & constant trigger window. In parallel, the scope waits for the trigger, captures the power consumption, and transfers the traces to the SCA evaluation framework. The ot-sca framework stores the trace as well as the cipher configuration in a database.
:--:
Figure: Power trace with AES encryption rounds visible (left). Aligned traces when zooming in (right).
The prior Figure depicts power traces captured during AES-GCM encryptions with the setup above. As shown in the figure, the traces are nicely aligned, allowing to perform a sound evaluation.
Methodology
To detect whether the hardened GHASH implementation effectively mitigates SCA attacks, the Test Vector Leakage Assessment (TVLA) approach discussed by Rambus in a whitepaper is adapted for the GCM mode of AES. In TVLA, Welchβs t-test is used to determine whether it is possible to statistically distinguish two power trace sets from each other. This test returns a value t for each sample, where a value of |t| > 4.5 means that, with a high probability, a data dependent leakage was detected. However, note that this test cannot provide any information whether the leakage is actually exploitable.
:--
Figure: TVLA plot showing leakage at around sample 1000. When increasing the number of traces (from 1000 to 10000), the leakage becomes more present. Note that the traces shown in this plot are taken from an arbitrary cryptographic hardware block and not AES.
The prior Figure shows a TVLA plot that will be used throughout this document. The red lines mark the Β± t-test border.
Dataset Generation for FvsR IV & Key
In TVLA, two different trace data sets need to be recorded. As described in the whitepaper, we generate these two trace data sets by using a fixed and a random AES-GCM cipher input set, i.e., the fixed and the random set.
| Input | Fixed Set | Random Set |
|---|---|---|
| Key | STATIC | RANDOM |
| IV | STATIC | RANDOM |
| PTX | STATIC | STATIC |
| AAD | STATIC | STATIC |
As shown in the table above, for our experiment we use a static cipher input for the fixed set. For the random set, we use a PRNG to randomly generate the secrets, i.e., key and IV, for each encryption. The dataset is generated directly on the device in the pentest framework. For each trace, ot-sca stores information to which dataset the trace belongs to.
With TVLA, the idea is to check whether we are able to distinguish power traces from the fixed and the random set.
Dataset Generation for FvsR PTX & AAD
For the second experiment, we use a static IV and key and calculate a FvsR PTX and AAD set:
| Input | Fixed Set | Random Set |
|---|---|---|
| Key | STATIC | STATIC |
| IV | STATIC | STATIC |
| PTX | STATIC | RANDOM |
| AAD | STATIC | RANDOM |
Results β FvsR IV & Key
In the following, we discuss the analysis results for each GCM phase. We start with the results for the FvsR IV & Key datasets.
:--:
Figure: AES-GCM block diagram. Red lines mark the trigger windows for each analysis step.
As shown in the prior Figure, we focus on analyzing (i) the generation of the hash subkey H, (ii) the encryption of the initial counter block S, (iii) the processing of the AAD blocks, (iv) the plaintext blocks, and (v) the tag generation. Each measurement is conducted with (a) masks off and (b) masks on to analyze the effectiveness of the masking countermeasure.
i) SCA Evaluation of Generating the Hash Subkey H
:--:
| Figure: Masking Off - 100k traces - Figure: Masking On - 1M traces |
Interpretation
The AES encryption is clearly visible in the form of 12 distinct peaks in the power traces shown in the prior set of Figures. The 12 peaks correspond to first the loading of the key and the all-zero block into the AES cipher core, followed by the initial round and the 10 full AES rounds (AES-128). They spread over approximately 470 samples which corresponds to the 56 target clock cycles a full AES-128 encryption takes.
If the masking is turned off (set of graphs), first and second-order leakage is clearly visible throughout the operation. If the masking is on (set of graphs), there is first-order leakage 1) at the beginning as well as 2) at the end of the operation.
- The leakage at the beginning of the operation is due to incrementing the IV/CTR value (inc32 function in GCM spec) which spreads across the first two AES rounds. This produces first-order leakage as the inc32 function implementation isnβt masked. It doesnβt need to be masked as the IV is not secret, just the encrypted initial counter block S (i.e., the encrypted IV) is secret in the context of GCM.
- The leakage at the end of the operation happens when the masked output of the AES cipher core, i.e., the masked hash subkey H, gets loaded in shares into the GHASH block. When studying the RTL, one can see that there is nothing in the path between the AES cipher core and the hash subkey registers inside the GHASH block that could combine the shares and cause this leakage. The leakage is most likely due to how the FPGA implementation tool maps the flip flops of the hash subkey register shares to the available FPGA logic slices: if flip flops of the different shares get mapped to the same logic slice, the carry-chain and other muxing logic present in the logic slice can combine the various inputs thereby causing SCA leakage despite these logic outputs not being used. Weβve observed similar effects in the past and there is research giving more insight into this and other FPGA-specific issues.
To summarize, the observed first-order leakage if masking is on is not of concern for ASIC implementations.
ii) SCA Evaluation of Encrypting the Initial Counter Block
:--:
| Figure: Masking Off - 100k traces - Figure: Masking On - 1M traces |
Interpretation
Again, the AES encryption is clearly visible in the form of 12 peaks in the power traces shown in the prior set of Figures. This AES encryption corresponds to the generation of the encrypted initial counter block S. The AES encryption is followed by another operation visible in the power trace: the computation of repeatedly used correction terms using the Galois-field multipliers inside GHASH. This operation takes 33 target clock cycles (approximately 275 samples).
If the masking is turned off (set of graphs), first and second-order leakage is clearly visible throughout both operations while being more pronounced during the GHASH operation. This is because the GHASH block is smaller and thus produces less noise. If the masking is on (set of graphs), there is first-order leakage 1) at the beginning as well as 2) between the two operations.
- As before, the leakage at the beginning of the operation is due to incrementing the IV/CTR value (inc32 function in GCM spec) which spreads across the first two AES rounds. This produces first-order leakage as the inc32 function implementation isnβt masked. It doesnβt need to be masked as the IV is not secret, just the encrypted initial counter block S (i.e., the encrypted IV) is secret in the context of GCM.
- As before, the leakage at the end of the operation happens when the masked output of the AES cipher core, i.e., the encrypted initial counter block gets loaded in shares into the GHASH block. When studying the RTL, one can see that there is nothing in the path between the AES cipher core and the GHASH state registers inside the GHASH block that could combine the shares and cause this leakage. As before, the leakage is most likely due to how the FPGA implementation tool maps the multiplexers in front of the GHASH state registers to the available FPGA logic slices: Since the multiplexers for both shares use the same control signals, the multiplexing logic can be combined even into the same look-up tables (LUTs) thereby causing SCA leakage. Weβve observed similar effects in the past and there is research giving more insight into this and other FPGA-specific issues.
To summarize, the observed first-order leakage if masking is on is not of concern for ASIC implementations.
iii) SCA Evaluation of Processing the AAD Blocks
Processing AAD Block 0
:--:
| Figure: Masking Off - 50k traces - Figure: Masking On - 10M traces |
Interpretation
For AAD blocks, the AES cipher core is not involved. However, during the computation of the first AAD block, the GHASH block needs to compute an additional correction term which is used for the very first block only. If the masking is turned off (first set of graphs), first- and second-order leakage is clearly visible but only for the first activity block. The second activity block involves computing the additional correction terms which requires Share 1 of the encrypted initial counter block to be multiplied by Share 1 of the hash subkey. But since the masking is off, both these values are zero for both the fixed and the random set and hence there is no SCA leakage. If the masking is turned on (second set of graphs), no SCA leakage is observable which is desirable.
Processing AAD Block 1
:--:
| Figure: Masking Off - 50k traces - Figure: Masking On - 10M traces |
Interpretation
For the second AAD block (and any subsequent AAD blocks) there is only one activity block corresponding to the Galois-field multiplication. If masking is turned off (first set of graphs), there is both first- and second-order leakage observable. If the masking is turned on (second set of graphs), no SCA leakage is observable which is desirable.
iv) SCA Evaluation of Processing the PTX Blocks
Processing PTX Block 0
:--:
| Figure: Masking Off - 50k traces - Figure: Masking On - 1M traces |
Interpretation
Like in ii) SCA Evaluation of Encrypting the Initial Counter Block there is first-order leakage 1) at the beginning and 2) between the two operations if the masking is turned on (first set of graphs).
- As before, the leakage at the beginning of the operation is due to incrementing the IV/CTR value (inc32 function in GCM spec) which spreads across the first two AES rounds. This produces first-order leakage as the inc32 function implementation isnβt masked. It doesnβt need to be masked as the IV is not secret, just the encrypted initial counter block S (i.e., the encrypted IV) is secret in the context of GCM.
- The leakage between the two operations is due to the unmasking of the AES cipher core output, the addition of input data to produce the ciphertext, and writing this value to the GHASH block and the output data registers. Itβs not related to the hash subkey H or the initial counter block S (i.e. the two secrets involved in the GHASH part of GCM). But since the AAD and the plaintext have been chosen to be the same for all traces in the fixed and the random sets, the traces of the fixed set only produce all the same ciphertext and thus are expected to exhibit a static power signature for this step, whereas the ciphertext of the random set is randomized through the random key and IV. However, since the ciphertext is not secret in the context of GCM, this leakage is of no concern.
To summarize, the observed first-order leakage if masking is on (second set of graphs) is not of concern.
Processing PTX Block 1
:--:
| Figure: Masking Off - 50k traces - Figure: Masking On - 1M traces |
Interpretation
As before (PTX Block 0), there is some first-order leakage observable when the masking is turned on. For the same reasons as before, this leakage is not of concern.
v) SCA Evaluation of the Tag Generation
:--:
| Figure: Masking Off - 50k traces - Figure: Masking On - 1M traces |
Interpretation
The generation of the final authentication tag consists of two operations.
- The 128-bit block containing the AAD and ciphertext lengths is hashed and the correction terms are added. The GHASH state is unmasked (still masked with the encrypted initial counter block S) and Share 1 of S is added to write the final authentication tag to the data output registers readable by software.
- In parallel to writing the final authentication tag to the data output registers, the internal state is all cleared to random values and an additional multiplication is triggered to clear the internal state of the Galois-field multipliers and the correction term registers.
If masking is turned off (first set of graphs), there is both first- and second-order leakage observable during the first activity block (tag generation) but not during the clearing operation. If the masking is turned on (second set of graphs), some SCA leakage is observable between the two operations, i.e., when the final authentication tag is written to the output data registers. This leakage is expected as both the fixed and the random data sets use a static AAD and plaintext. This means, the tag for the fixed data set is fixed whereas the tags for the random set get randomized through the ciphertext (random due to the random key and IV).
To summarize, the observed first-order leakage if masking is on (second set of graphs) is not of concern.
Results β FvsR PTX & AAD
In the following, we discuss the analysis results for each FvsR PTX & AAD datasets. These experiments were specifically done to investigate leakage peaks identified for the FvsR Key & IV datasets that are attributed to how the FPGA implementation tool maps flip flops and multiplexer shares to the available FPGA logic slices.
i) SCA Evaluation of Generating the Hash Subkey H
:--:
| Figure: Masking Off - 50k traces - Figure: Masking On - 1M traces |
Interpretation
There is no SCA leakage visible in both cases without masking (first set of graphs) and with masking turned on (second set of graphs). This is expected as the hash subkey generation doesnβt involve the plaintext and the AAD but only the key and IV. Both the fixed and random set use the same static key and IV.
This experiment was specifically done to check whether the leakage identified in i) SCA Evaluation of Generating the Hash Subkey H and attributed to how the FPGA implementation tool maps the flip flops of the hash subkey register shares to the available FPGA logic slices. As expected, the leakage peak is now gone.
ii) SCA Evaluation of Encrypting the Initial Counter Block
:--:
| Figure: Masking Off - 50k traces - Figure: Masking On - 1M traces |
Interpretation
There is no SCA leakage visible in both cases without masking (first set of graphs) and with masking turned on (second set of graphs). This is expected as the encryption of the initial counter block and the subsequent computation of repeatedly used correction terms doesnβt involve the plaintext and the AAD but only the key and IV. Both the fixed and random set use the same static key and IV.
This experiment was specifically done to check whether the leakage identified in ii) SCA Evaluation of Encrypting the Initial Counter Block and attributed to how the FPGA implementation tool maps the multiplexers in front of the GHASH state registers to the available FPGA logic slices. As expected, the leakage peak is now gone.
iv) SCA Evaluation of Processing the PTX Block 0
:--:
| Figure: Masking Off - 100k traces - Figure: Masking On - 1M traces |
Interpretation
With the masking turned off (first set of graphs), there is first-order leakage 1) at the beginning of the operation and 2) throughout the entire GHASH operation.
- The leakage at the beginning of the operation is due to the input data (the plaintext) being written to an internal buffer register. The AES cipher is operated in counter mode, meaning it doesnβt encrypt the input data but the counter value (incremented IV). Because the IV is fixed for both the fixed and the random data set, no leakage is observed during the AES encryption even if the masking is off. At the end of the AES encryption, the output of the AES cipher core is added to the content of the buffer register to produce the ciphertext which is then forwarded to the GHASH block and to the data output registers.
- The GHASH operation then processes this ciphertext. The observed leakage when the masking is off is expected.
With the masking turned on (second set of graphs), the first-order leakage at the beginning of the operation remains visible. The reason for this is that the internal register buffering the previous input data is not masked. This is of no concern as the leakage is not related to key or IV.
Another first-order leakage peak is visible between the AES encryption and the GHASH operation. This leakage is due to the unmasked AES cipher core output being added to the input data (coming from the internal buffer register) and the result being stored to the output data register. As key and IV are static and identical for both the fixed and the random data set, the cipher core output is the same for both sets. Any difference in the power signature between the two sets is due to the different plaintext / ciphertext. Again, this is to be expected and of no concern as the ciphertext is not secret in the context of GCM.
Reproducing the FPGA Experiments
Prerequisites
(i) Setting up the CW310 and CW Husky
Please follow the guide here to prepare the CW310 and CW Husky for the SCA measurements.
(ii) Generating the FPGA Bitstream
Follow the guide here to install Xilinx Vivado. Please note that a valid license is needed to generate bitstreams for the CW310 FPGA board.
Then, build the bitstream from the aes-gcm-sca-bitstream branch. This branch includes the AES-GCM and applies several optimizations (disabling certain features to reduce the area utilization) to improve the SCA measurements.
git clone https://github.com/vogelpi/opentitan.git
cd opentitan
git checkout aes-gcm-sca-bitstream
./bazelisk.sh build //hw/bitstream/vivado:fpga_cw310_test_rom
cp bazel-bin/hw/bitstream/vivado/build.fpga_cw310/synth-vivado/lowrisc_systems_chip_earlgrey_cw310_0.1.bit .
The resulting bitstream is lowrisc_systems_chip_earlgrey_cw310_0.1.bit.
(iii) Compiling the Penetration Testing Binary
The penetration testing binary that is running on the target is the framework that receives commands from the side-channel evaluation framework and triggers the AES-GCM operations.
git clone <https://github.com/vogelpi/opentitan.git>
cd opentitan
git checkout aes-gcm-review
./bazelisk.sh build //sw/device/tests/penetrationtests/firmware:firmware_fpga_cw310_test_rom
cp bazel-bin/sw/device/tests/penetrationtests/firmware/firmware_fpga_cw310_test_rom_fpga_cw310_test_rom.bin sca_ujson_fpga_cw310.bin
The resulting penetration testing binary is sca_ujson_fpga_cw310.bin.
(iv) Setting up the Side-Channel Evaluation Framework
Clone the ot-sca repository and switch to the dedicated AES-GCM branch:
git clone <https://github.com/lowRISC/ot-sca.git>
cd ot-sca
git checkout ot-sca-aes-gcm
Then, follow this guideline to prepare your machine for the measurements.
Afterwards, copy the bitstream to ot-sca/objs/lowrisc_systems_chip_earlgrey_cw310_0.1.bit and the binary to ot-sca/objs/sca_ujson_fpga_cw310.bin.
Finally, determine the port the CW310 opened on your machine (e.g., /dev/ttyACM2) and set it accordingly in the port field of the ot-sca/capture/configs/aes_gcm_sca_cw310.yaml configuration file.
Capturing Traces
After fulfilling the prerequisites, traces can be captured using ot-sca.
To configure the measurement, adapt the script located in ot-sca/capture/configs/aes_gcm_sca_cw310.yaml.
The following parameters can be changed:
husky:
# Number of encryptions performed in one batch.
num_segments: 35
# Number of cycles that are captured by the CW Husky.
num_cycles: 320
capture:
# Number of traces to capture.
num_traces: 100000
# Number of traces to keep in memory before flushing to the disk.
trace_threshold: 50000
test:
# Values used for the fixed set.
iv_fixed: [0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xAD, 0xF0, 0xCA,
0xCC, 0x1A, 0x00, 0x00, 0x00, 0x00]
key_fixed: [0x81, 0x1E, 0x37, 0x31, 0xB0, 0x12, 0x0A, 0x78, 0x42, 0x78,
0x1E, 0x22, 0xB2, 0x5C, 0xDD, 0xF9]
# Static values that are used by the fixed and the random set.
ptx_blocks: 2
ptx_static: [[0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA], [0xBB, 0xBB, 0xBB,
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
0xBB, 0xBB, 0xBB]]
ptx_last_block_len_bytes: 16
aad_blocks: 2
aad_static: [[0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC], [0xDD, 0xDD, 0xDD,
0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
0xDD, 0xDD, 0xDD, 0xDD]]
aad_last_block_len_bytes: 16
# Trigger configuration (select only one).
# [Hash sub key, Init. block, AAD block, PTX block, TAG block]
triggers: [False, False, False, False, True]
# Which AAD or PTX block. 0 = first block.
trigger_block: 0
# 32-bit seed for masking on device. To switch off the masking, use 0
# as an LFSR seed.
lfsr_seed: 0x00000000
#lfsr_seed: 0xdeadbeef
After tweaking the configuration, the traces can be captured by executing:
cd capture
./capture_aes_gcm.py -c configs/aes_gcm_sca_cw310.yaml -p aes_gcm_sca
Where the -c parameter is the config and -p the database where the traces are stored.
Performing the TVLA
After capturing the traces, the TVLA can be performed by switching into the ot-sca/analysis folder, copying the ot-sca/analysis/configs/tvla_cfg_kmac.yaml file to ot-sca/analysis/configs/tvla_cfg_aes_gcm.yaml, and modifying the configuration file:
project_file: ../capture/projects/aes_gcm_sca
trace_file: null
trace_start: null
trace_end: null
leakage_file: null
save_to_disk: null
save_to_disk_ttest: null
round_select: null
byte_select: null
input_histogram_file: null
output_histogram_file: null
number_of_steps: 1
ttest_step_file: null
plot_figures: true
test_type: "GENERAL_KEY"
mode: aes
filter_traces: true
trace_threshold: 50000
trace_db: ot_trace_library
By calling
./tvla.py --cfg-file tvla_cfg_aes_gcm.yaml run-tvla
the TVLA plot is generated.
PCR vault
- Platform Configuration Register (PCR) vault is a register file that stores measurements to be used by the microcontroller.
- PCR entries are read-only registers of 512 bits each.
- Control bits allow for entries to be cleared by FW, which sets their values back to 0.
- A lock bit can be set by FW to prevent the entry from being cleared. The lock bit is sticky and only resets on a powergood cycle.
| PCRV register | Address Offset | Description |
|---|---|---|
| PCR Control[31:0] | 0x1001a000 | 32 Control registers, 32 bits each |
| PCR Entry[31:0][11:0][31:0] | 0x1001a600 | 32 PCR entries, 384 bits each |
PCR vault functional block
PCR entries are hash extended using a hash extension function. The hash extension function takes the data currently in the PCR entry specified, concatenates data provided by the FW, and performs a SHA384 function on that data, storing the result back into the same PCR entry.
PCR hash extend function
FW provides the PCR entry to use as source and destination of the hash extend. HW copies the PCR into the start of the SHA block and locks those dwords from FW access. FW then provides the new data, and runs the SHA function as usual. After initialization, the locked dwords are unlocked.
FW must set a last cycle flag before running the last iteration of the SHA engine. This could be the first βinitβ cycle, or the Nth βnextβ cycle. This flag allows HW to copy the final resulting hash output back to the source PCR.
PCR signing
- PCR signing uses the key in key slot index 7 for PCR signing
- HW implements a HW function called GEN_PCR_HASH
- HW reads all the PCRs from all the PCR slots and hash extends them along with the NONCE that Caliptra FW provides
- PCR Hash = Hash(PCR[0], β¦, PCR[31], Nonce)
- HW also implements a HW function called SIGN_PCR. This function takes the PCR digest that was generated by the previous routine and signs it using the key in key slot 7, following the same ECC sign flow defined in the ECC section.
- The resulting PCR DIGEST is used only once for signing by the HW. If a new PCR signing is required, GEN_PCR_HASH needs to be redone.
Key vault
Key Vault (KV) is a register file that stores the keys to be used by the microcontroller, but this register file is not observed by the microcontroller. Each cryptographic function has a control register and functional block designed to read from and write to the KV.β―
| KV register | Description |
|---|---|
| Key Control[23:0] | 24 Control registers, 32 bits each |
| Key Entry[23:0][15:0][31:0] | 24 Key entries, 512 bits each No read or write access |
Key vault functional block
Keys and measurements are stored in 512b register files. These have no read or write path from the microcontroller. The entries are read through a passive read mux driven by each cryptographic block. Locked entries return zeroes.β―
Entries in the KV must be cleared via control register, or by de-assertion of pwrgood.β―β―
Each entry has a control register that is writable by the microcontroller.β―
The destination valid field is programmed by FW in the cryptographic block generating the key, and it is passed here at generation time. This field cannot be modified after the key is generated and stored in the KV.β―
| KV Entry Ctrl Fields | Reset | Description |
|---|---|---|
| Lock wr[0] | core_only_rst_b | Setting the lock wr field prevents the entry from being written by the microcontroller. Keys are always locked. After a lock is set, it cannot be reset until cptra_rst_b is de-asserted. |
| Lock use[1] | core_only_rst_b | Setting the lock use field prevents the entry from being used in any cryptographic blocks. After the lock is set, it cannot be reset until cptra_rst_b is de-asserted. |
| Clear[2] | cptra_rst_b | If unlocked, setting the clear bit causes KV to clear the associated entry. The clear bit is reset after entry is cleared. |
| rsvd0[3] | ||
| rsvd1[8:4] | ||
| Dest_valid[16:9] | hard_reset_b | KV entry can be used with the associated cryptographic block if the appropriate index is set. [0] - HMAC KEY [1] - HMAC BLOCK [2] - MLDSA SEED [3] - ECC PRIVKEY [4] - ECC SEED [5] - AES KEY [6] - MLKEM SEED [7] - MLKEM MSG [8] - AXI DMA DATA |
| last_dword[20:19] | hard_reset_b | Store the offset of the last valid dword, used to indicate the last cycle for read operations. |
Key vault cryptographic functional blockβ―
A generic block is instantiated in each cryptographic block to enable access to KV.β―
Each input to a cryptographic engine can have a key vault read block associated with it. The KV read block takes in a key vault read control register that drives an FSM to copy an entry from the key vault into the appropriate input register of the cryptographic engine.
Each output generated by a cryptographic engine can have its result copied to a slot in the key vault. The KV write block takes in a key vault write control register. This register drives an FSM to copy the result from the cryptographic engine into the appropriate key vault entry. It also programs a control field for that entry to indicate where that entry can be used.
After programming the key vault read control, FW needs to query the associated key vault read status to confirm that the requested key was copied successfully. After valid is set and the error field reports success, the key is ready to be used.
Similarly, after programming the key vault write control and initiating the cryptographic function that generates the key to be written, FW needs to query the associated key vault write status to confirm that the requested key was generated and written successfully.
When a key is read from the key vault, the API register is locked and any result generated from the cryptographic block is not readable by firmware. The digest can only be sent to the key vault by appropriately programming the key vault write controls. After the cryptographic block completes its operation, the lock is cleared and the key is cleared from the API registers.
If multiple iterations of the cryptographic function are required, the key vault read and write controls must be programmed for each iteration. This ensures that the lock is set and the digest is not readable.
The following tables describe read, write, and status values for key vault blocks.
| KV Read Ctrl Reg | Description |
|---|---|
| read_en[0] | Indicates that the read data is to come from the key vault. Setting this bit to 1 initiates copying of data from the key vault. |
| read_entry[5:1] | Key vault entry to retrieve the read data from the engine. |
| pcr_hash_extend[6] | Requested entry is a PCR. This is used only for the SHA engine to hash extend. It is not functional in any other cryptographic engine. |
| rsvd[31:7] | Reserved field |
| KV Write Ctrl Reg | Description |
|---|---|
| write_en[0] | Indicates that the result is to be stored in the key vault. Setting this bit to 1 copies the result to the key vault when it is ready. |
| write_entry[5:1] | Key vault entry to store the result. |
| hmac_key_dest_valid[6] | HMAC KEY is a valid destination. |
| hmac_block_dest_valid[7] | HMAC BLOCK is a valid destination. |
| mldsa_seed_dest_valid[8] | MLDSA SEED is a valid destination. |
| ecc_pkey_dest_valid[9] | ECC PKEY is a valid destination. |
| ecc_seed_dest_valid[10] | ECC SEED is a valid destination. |
| aes_key_dest_valid[11] | AES KEY is a valid destination. |
| mlkem_seed_dest_valid[12] | MLKEM SEED is a valid destination. |
| mlkem_msg_dest_valid[13] | MLKEM MSG is a valid destination. |
| dma_data_dest_valid[14] | DMA DATA is a valid destination. |
| rsvd[31:15] | Reserved field |
| KV Status Reg | Description |
|---|---|
| ready[0] | Key vault control is idle and ready for a command. |
| valid[1] | Requested flow is done. |
| error[9:2] | SUCCESS - 0x0 - Key Vault flow was successful KV_READ_FAIL - 0x1 - Key Vault Read flow failed KV_WRITE_FAIL - 0x2 - Key Vault Write flow failed |
De-obfuscation engine
To protect software intellectual property from different attacks and, particularly, for thwarting an array of supply chain threats, code obfuscation is employed. Hence, the de-obfuscation engine is implemented to decrypt the code.
Advanced Encryption Standard (AES) is used as a de-obfuscation function to encrypt and decrypt data [4]. The hardware implementation is based on Secworks/aes [1]. The implementation supports the two variants: 128- and 256-bit keys with a block/chunk size of 128 bits.
The AES algorithm is described as follows:
- The key is fed to an AES core to compute and initialize the round key
- The message is broken into 128-bit chunks by the host
- For each chunk:
- The message is fed to the AES core
- The AES core and its working mode (enc/dec) are triggered by the host
- The AES core status is changed to ready after encryption or decryption processing
- The result digest can be read before processing the next message chunks
Key vault de-obfuscation block operation
A de-obfuscation engine (DOE) is used in conjunction with AES cryptography to de-obfuscate the UDS and field entropy and HEK seed.β―β―
- The obfuscation key is wired to DOE engine. The data to be decrypted (either obfuscated UDS, obfuscated field entropy, or obfuscated HEK seed) is fed into the DOE data.
- An FSM manually drives the DOE engine and writes the decrypted data back to the key vault.β―
- FW programs the DOE with the requested function (UDS, field entropy, or HEK seed de-obfuscation), and the destination for the result.β―
- After de-obfuscation is complete, FW can clear out the UDS, field entropy, and HEK seed values from any flops until cptra_pwrgood de-assertion.β―β―
The following tables describe DOE register and control fields.
| DOE Register | Address | Description |
|---|---|---|
| IV | 0x10000000 | 128 bit IV for DOE flow. Stored in big-endian representation. |
| CTRL | 0x10000010 | Controls for DOE flows. |
| STATUS | 0x10000014 | Valid indicates the command is done and results are stored in key vault. Ready indicates the core is ready for another command. |
| DOE Ctrl Fields | Reset | Description |
|---|---|---|
| CMD[1:0] | Cptra_rst_b | 2βb00 Idle 2βb01 Run UDS flow 2βb10 Run FE flow 2βb11 Clear Obf Secrets |
| DEST[6:2] | Cptra_rst_b | Destination register for the result of the de-obfuscation flow. Field entropy writes into DEST and DEST+1 Key entry only, canβt go to PCR . |
| CMD_EXT[8:7] | Cptra_rst_b | 2βb00 Idle (or running a standard, non-extended command) 2βb01 Run OCP LOCK HEK seed flow 2βb10 RESERVED 2βb11 RESERVED |
Key vault de-obfuscation flowβ―
- ROM loads IV into DOE. ROM writes to the DOE control register the destination for the de-obfuscated result and sets the appropriate bit to run UDS, field entropy, and/or HEK seed flow.β―
- DOE state machine takes over and loads the Caliptra obfuscation key into the key register.β―
- Next, either the obfuscated UDS or field entropy are loaded into the block register 4 DWORDS at a time.β―
- Results are written to the KV entry specified in the DEST field of the DOE control register.β―
- State machine resets the appropriate RUN bit when the de-obfuscated key is written to KV. FW can poll this register to know when the flow is complete.
- The clear obf secrets command flushes the obfuscation key, the obfuscated UDS, and the field entropy from the internal flops. This should be done by ROM after both de-obfuscation flows are complete.
Data vault
Data vault is a set of generic scratch pad registers with specific lock functionality and clearable on cold and warm resets.
- 48B scratchpad registers that are lockable but cleared on cold reset (10 registers)
- 48B scratchpad registers that are lockable but cleared on warm reset (10 registers)
- 4B scratchpad registers that are lockable but cleared on cold reset (8 registers)
- 4B scratchpad registers that are lockable but cleared on warm reset (10 registers)
- 4B scratchpad registers that are cleared on warm reset (8 registers)
OCP LOCK Hardware Architecture
Overview
The following hardware and ROM/FW enhancements support the OCP L.O.C.K. (a.k.a. OCP LOCK) flows defined for SSD applications. The specification is available here:
OCP LOCK Spec
Additional Registers, Straps, and Macros for OCP LOCK
-
SS_OCP_LOCK_CTRL.LOCK_IN_PROGRESS
A status/control bit used to enforce the new KeyVault (KV) rules required by OCP LOCK. Write-1-to-set, meaning that, once-enabled, OCP LOCK functionality will persist until the register is cleared by a cold reset. See the dedicated section below for details on the behaviors this register enables. -
ss_ocp_lock_en(constant-value input strap) with a corresponding bit inCPTRA_HW_CONFIGregister namedOCP_LOCK_MODE_en:- Enables Caliptra ROM to perform OCP LOCK operations (e.g., using DOE for HEK seed de-obfuscation, Key Release via AXI DMA).
- Allows the ROM to set
SS_OCP_LOCK_CTRL.LOCK_IN_PROGRESS. ss_ocp_lock_enis a strap pin and must be driven with a constant value by the integrator.CPTRA_HW_CONFIGregister samples this strap and store its value inOCP_LOCK_MODE_enbit- This bit is only reflected in CPTRA_HW_CONFIG if CALIPTRA_MODE_SUBSYSTEM is defined
-
HEK seed fuse register
Holds the obfuscated HEK seed. ROM is responsible for performing the operation to de-obfuscate the HEK seed. -
Key release address and size straps
Writable untilFUSE_WR_DONE, then locked (same as fuses and other subsystem-mode straps).- Address strap (
strap_ss_key_release_base_addr): full destination address for key release; in OCP LOCK this is the destination for the MEK to be written. Firmware can derive the SFR base from this value as needed. - Size strap (
strap_ss_key_release_key_size): byte-count (dword-aligned count is required by HW) of the key to program to the destination address via the key release operation. Strap input values are forced to a dword value by hardware. If control firmware updates this value (prior to FUSE_WR_DONE being set), it must use a dword-aligned value.
- Address strap (
Refer to the Caliptra Integration Spec for more details about macros and strap pins.
SS_OCP_LOCK_CTRL.LOCK_IN_PROGRESS Register Bit
When/How it is set
- Set by Caliptra ROM after performing OCP LOCK-related derivations (HEK, MDK, etc.).
- Can be set iff (
ss_ocp_lock_enis set to 1 ANDCALIPTRA_MODE_SUBSYSTEMis defined). - Once set, a value of 1 persists until the register is cleared by cold reset.
Enforcements/Effects
- Reserves KeyVault slots 0β15 for standard use-cases.
- Reserves KeyVault slots 16β23 for OCP LOCK use-cases.
- Key Vault slot 16 (KV16) is reserved for holding the MDK
- Key Vault slot 23 (KV23) is reserved for holding the MEK
- Blocks interactions between standard slots and LOCK slots. This means that any crypto operation that uses a Key Vault input value (e.g. for Key, Block, Seed inputs) may not write the output to a Key Vault from a different region. E.g., When
SS_OCP_LOCK_CTRL.LOCK_IN_PROGRESSis set, HMAC may not perform an operation that uses Key Vault slot 8 as BLOCK input and writes the output TAG to Key Vault slot 17. - Enables Key Release via AXI DMA.
- Enables AES engine to write output to Key Vault, which must use KV23.
Note: If
SS_OCP_LOCK_CTRL.LOCK_IN_PROGRESSis1, it also impliesss_ocp_lock_enandCALIPTRA_MODE_SUBSYSTEMare also1.
AES Write Path
- MEK is the final OCP LOCK key. It is decrypted and stored in KV23. After decryption, MEK may be transferred to its destination (as specified by the input strap) via AXI DMA.
- OCP LOCK requires both the AES write path and a DMA path to the MEK destination.
- Hardware enforcement: MEK is written to KV23. Hardware recognizes the MEK generation request if there is an AES-ECB decrypt operation with KV16 (MDK) as the AES-ECB key and routes the result accordingly. In this case, output of the decrypted plaintext via the AES dataout register API is blocked. Any Key Vault write operation requested for the AES output that does not meet these requirements results in a Key Vault write failure status.
KeyVault Access Rules & Filtering (when LOCK_IN_PROGRESS is set)
- KV23 (MEK destination): write-restricted to AES only.
- KV22 (HEK): locked for writes until warm reset (ROM requirement).
- KV16 (MDK): locked for writes until warm reset (ROM requirement).
- If OCP LOCK mode is enabled:
- KV23 must not be used as input to other crypto operationsβonly as a Key Release source.
- AES-ECB decrypt with key = KV16 must have dest = KV23; otherwise the destination is FW.
Rationale: Prevents malicious FW from writing known values into other KV slots via AES.
- Additional KV behaviors
- On write, hardware validates that the destination is legal for the source/read. If not valid, the Key Vault write operation returns a failing status.
- No parallel crypto operations permitted for cryptographic blocks with access to Key Vault. KV does not track this; Caliptra enforces this rule by evaluating each block's busy status indicator and signaling violations through the CPTRA_HW_ERROR_FATAL register and corresponding interrupt at Caliptra top level design.
HEK Seed Deβobfuscation
- Executed by Caliptra ROM. The DOE supports a HEK deobfuscation command that may be executed only once during a boot cycle. If Caliptra ROM does not run this flow to produce the HEK seed, it should run the flow with a dummy Key Vault slot to lock against future erroneous uses.
- Hardware-supported HEK seed Deobfuscation Path: Ratchet Fuse Register (obfuscated HEK seed) β DOE (with
OBF_KEY) β KV slot 22 (de-obfuscated seed). - Caliptra ROM shall lock KV22 for writes immediately it has derived the HEK into that slot.
Key Release
Caliptra's AXI DMA supports a hardware path to write KV23 (MEK) to the SoC via the AXI manager interface. The following rules constrain this operation:
- Allowed only when
SS_OCP_LOCK_CTRL.LOCK_IN_PROGRESS(sticky W1SET) is set by Caliptra ROM. - Destination and size must match the values from the straps:
strap_ss_key_release_base_addrstrap_ss_key_release_key_size
Additional Security Hardening Specific to OCP LOCK Enhancements
Scan/Debug Protections
- Flush DMA FIFOs to prevent leakage of secrets via scan chain.
- Flush AES β KV interface.
AES/KV/DMA Robustness
- AES β KV write path: The key can't be written to KeyVault unless key_size bytes are decrypted by AES.
- Validate DMA
key_size; error ifkey_size > 512b. - Avoid hangs when
key_size!= KV read DWORD count:- On KV reads, if
key_sizeis smaller than the KV entry, drop extra data (do not push to FIFO).
- On KV reads, if
- DMA KV read error: Raised on the first transfer cycle from KV to DMA; DMA transitions immediately to
DMA_ERRORwithout issuing an AXI transfer. - KV write enable sourced from AES (during OCP LOCK) so it cannot be modified mid-transfer.
- Enable AES β KV write path only if
SS_OCP_LOCK_CTRL.LOCK_IN_PROGRESSis set.
Cryptographic blocks fatal and non-fatal errors
The following table describes cryptographic errors.
| Errors | Error type | Description |
|---|---|---|
| ECC_R_ZERO | HW_ERROR_NON_FATAL | Indicates a non-fatal error in ECC signing if the computed signature R is equal to 0. FW should change the message or privkey to perform a valid signing. |
| CRYPTO_ERROR | HW_ERROR_FATAL | Indicates a fatal error due to multiple cryptographic operations occurring simultaneously. FW must only operate one cryptographic engine at a time. |
Terminology
The following terminology is used in this document.
| Abbreviation | Description |
|---|---|
| AES | Advanced Encryption Standard |
| BMC | Baseboard Management Controller |
| CA | Certificate Authority |
| CDI | Composite Device Identifier |
| CPU | Central Processing Unit |
| CRL | Certificate Revocation List |
| CSR | Certificate Signing Request |
| CSP | Critical Security Parameter |
| DICE | Device Identifier Composition Engine |
| DME | Device Manufacturer Endorsement |
| DPA | Differential Power Analysis |
| DRBG | Deterministic Random Bit Generator |
| DWORD | 32-bit (4-byte) data element |
| ECDSA | Elliptic Curve Digital Signature Algorithm |
| ECDH | Elliptic Curve Deffie-Hellman Key Exchange |
| FMC | FW First Mutable Code |
| FSM | Finite State Machine |
| GPU | Graphics Processing Unit |
| HMAC | Hash-based message authentication code |
| IDevId | Initial Device Identifier |
| iRoT | Internal RoT |
| IV | Initial Vector |
| KAT | Known Answer Test |
| KDF | Key Derivation Function |
| LDevId | Locally Significant Device Identifier |
| MCTP | Management Component Transport Protocol |
| NIC | Network Interface Card |
| NIST | National Institute of Standards and technology |
| OCP | Open Compute Project |
| OTP | One-time programmable |
| PCR | Platform Configuration Register |
| PKI | Public Key infrastructure |
| PUF | Physically unclonable function |
| RNG | Random Number Generator |
| RoT | Root of Trust |
| RTI | RoT for Identity |
| RTM | RoT for Measurement |
| RTR | RoT for Reporting |
| SCA | Side-Channel Analysis |
| SHA | Secure Hash Algorithm |
| SoC | System on Chip |
| SPA | Simple Power Analysis |
| SPDM | Security Protocol and Data Model |
| SSD | Solid State Drive |
| TCB | Trusted Computing Base |
| TCI | TCB Component Identifier |
| TCG | Trusted Computing Group |
| TEE | Trusted Execution Environment |
| TRNG | True Random Number Generator |
| UECC | Uncorrectable Error Correction Code |
References
- J. StrΓΆmbergson, "Secworks," [Online]. Available at https://github.com/secworks.
- NIST, Federal Information Processing Standards Publication (FIPS PUB) 180-4 Secure Hash Standard (SHS).
- OpenSSL [Online]. Available at https://www.openssl.org/docs/man3.0/man3/SHA512.html.
- N. W. Group, RFC 3394, Advanced Encryption Standard (AES) Key Wrap Algorithm, 2002.
- NIST, Federal Information Processing Standards Publication (FIPS) 198-1, The Keyed-Hash Message Authentication Code, 2008.
- N. W. Group, RFC 4868, Using HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512 with IPsec, 2007.
- RFC 6979, Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA), 2013.
- TCG, Hardware Requirements for a Device Identifier Composition Engine, 2018.
- Coron, J.-S.: Resistance against differential power analysis for elliptic curve cryptosystems. In: KoΒΈc, CΒΈ .K., Paar, C. (eds.) CHES 1999. LNCS, vol. 1717, pp. 292β302.
- Schindler, W., Wiemers, A.: Efficient side-channel attacks on scalar blinding on elliptic curves with special structure. In: NISTWorkshop on ECC Standards (2015).
- National Institute of Standards and Technology, "Digital Signature Standard (DSS)", Federal Information Processing Standards Publication (FIPS PUB) 186-4, July 2013.
- NIST SP 800-90A, Rev 1: "Recommendation for Random Number Generation Using Deterministic Random Bit Generators", 2012.
- CHIPS Alliance, βRISC-V VeeR EL2 Programmerβs Reference Manualβ [Online] Available at https://github.com/chipsalliance/Cores-VeeR-EL2/blob/main/docs/RISC-V_VeeR_EL2_PRM.pdf.
- βThe RISC-V Instruction Set Manual, Volume I: User-Level ISA, Document Version 20191213β, Editors Andrew Waterman and Krste Asanovi Μc, RISC-V Foundation, December 2019. Available at https://riscv.org/technical/specifications/.
- βThe RISC-V Instruction Set Manual, Volume II: Privileged Architecture, Document Version 20211203β, Editors Andrew Waterman, Krste Asanovi Μc, and John Hauser, RISC-V International, December 2021. Available at https://riscv.org/technical/specifications/.
- NIST SP 800-56A, Rev 3: "Recommendation for Pair-Wise Key-Establishment Schemes Using Discrete Logarithm Cryptography", 2018.
- NIST FIPS 202: "SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions", 2015. Available at: https://csrc.nist.gov/pubs/fips/202/final.
[1] Caliptra.** **Spanish for βroot capβ and describes the deepest part of the root
76a7d85

Caliptra Integration Specification
Version 2.1
Scope
This document describes the Caliptra hardware implementation requirements, details, and release notes. This document is intended for a high-level overview of the IP used in Caliptra.
This document is not intended for any micro-architectural design specifications. Detailed information on each of the IP components are shared in individual documents, where applicable.
Overview
This document contains high level information on the Caliptra hardware design. The details include open-source IP information, configuration settings for open-source IP (if applicable), and IP written specifically for Caliptra.
For more information, see Caliptra: A Datacenter System on a Chip (SoC) Root of Trust (RoT).
References and related specifications
The blocks described in this document are either obtained from open-source GitHub repositories, developed from scratch, or modification of open-source implementations. Links to relevant documentation and GitHub sources are shared in the following table.
Table 1: Related specifications
| IP/Block | GitHub URL | Documentation | Link |
|---|---|---|---|
| Cores-VeeR | GitHub - chipsalliance/Cores-VeeR-EL2 | VeeR EL2 Programmerβs Reference Manual | chipsalliance/Cores-VeeR-EL2 Β· GitHubPDF |
| AHB Lite Bus | aignacio/ahb_lite_bus: AHB Bus lite v3.0 (github.com) | AHB Lite Protocol Figure: SoC interface block diagram | ahb_lite_bus/docs at master Β· aignacio/ahb_lite_bus (github.com) ahb_lite_bus/diagram_ahb_bus.png at master Β· aignacio/ahb_lite_bus (github.com) |
| SHA 256 | secworks/sha256: Hardware implementation of the SHA-256 cryptographic hash function (github.com) | ||
| SHA 512 | |||
| SPI Controller | https://github.com/pulp-platform/axi_spi_master |
Caliptra Core
For information on the Caliptra Core, see the High level architecture section of Caliptra: A Datacenter System on a Chip (SoC) Root of Trust (RoT).
SoC interface definition
The following figure shows the SoC interface definition.
Figure: SoC Interface Block Diagram

Integration parameters
The following table describes integration parameters.
Table 2: Integration parameters
| Parameter name | Width | Defines file | Description |
|---|---|---|---|
| CPTRA_SET_MBOX_AXI_USER_INTEG | 5 | soc_ifc_pkg.sv | Each bit hardcodes the valid AXI_USER for mailbox at integration time. |
| CPTRA_MBOX_VALID_AXI_USER | [4:0][31:0] | soc_ifc_pkg.sv | Each parameter corresponds to a hardcoded valid AXI_USER value for mailbox, set at integration time. Must set corresponding bit in the CPTRA_SET_MBOX_AXI_USER_INTEG parameter for this valid axi user override to be used. CANNOT use value 0xFFFFFFFF. This is reserved for Caliptra-internal usage. |
| CPTRA_DEF_MBOX_VALID_AXI_USER | 32 | soc_ifc_pkg.sv | Sets the default valid AXI_USER for mailbox accesses. This AXI_USER is valid at all times. CANNOT use value 0xFFFFFFFF. This is reserved for Caliptra-internal usage. |
| CPTRA_SET_FUSE_AXI_USER_INTEG | 1 | soc_ifc_pkg.sv | Sets the valid AXI_USER for fuse accesses at integration time. |
| CPTRA_FUSE_VALID_AXI_USER | 32 | soc_ifc_pkg.sv | Overrides the programmable valid AXI_USER for fuse accesses when CPTRA_SET_FUSE_AXI_USER_INTEG is set to 1. CANNOT use value 0xFFFFFFFF. This is reserved for Caliptra-internal usage. |
Table 3: Integration Defines
| Defines | Defines file | Description |
|---|---|---|
| CALIPTRA_FUSE_GRANULARITY_32 | config_defines.svh | Defining this means fuse row granularity is 32-bits. If not defined it means fuse row granularity is 64 bits. If defined, the CPTRA_HW_CONFIG.CALIPTRA_FUSE_GRANULARITY_32 SOC_IFC register bit is set to 1 for 32-bit granularity. Otherwise, it is set to 0 for 64-bit granularity. This is used by Caliptra ROM for UDS and Field Entropy provisioning. |
| CALIPTRA_INTERNAL_TRNG | config_defines.svh | Defining this enables the internal TRNG source. This must be set to 1 in Subsystem mode. |
| CALIPTRA_MODE_SUBSYSTEM | config_defines.svh | Defining this enables Caliptra to operate in Subsystem mode. This includes features such as the debug unlock flow, AXI DMA (for recovery flow), Subsystem-level straps, among other capabilites. See Caliptra Subsystem Architectural Flows for more details |
| USER_ICG | config_defines.svh | If added by an integrator, provides the name of the custom clock gating module that is used in clk_gate.sv. USER_ICG replaces the clock gating module, CALIPTRA_ICG, defined in caliptra_icg.sv. This substitution is only performed if integrators also define TECH_SPECIFIC_ICG. |
| TECH_SPECIFIC_ICG | config_defines.svh | Defining this causes the custom, integrator-defined clock gate module (indicated by the USER_ICG macro) to be used in place of the native Caliptra clock gate module. |
| USER_EC_RV_ICG | config_defines.svh | If added by an integrator, provides the name of the custom clock gating module that is used in the RISC-V core. USER_EC_RV_ICG replaces the clock gating module, TEC_RV_ICG, defined in beh_lib.sv. This substitution is only performed if integrators also define TECH_SPECIFIC_EC_RV_ICG. |
| TECH_SPECIFIC_EC_RV_ICG | config_defines.svh | Defining this causes the custom, integrator-defined clock gate module (indicated by the USER_EC_RV_ICG macro) to be used in place of the native RISC-V core clock gate module. |
| CALIPTRA_AXI_DMA_ADDR_WIDTH | config_defines.svh | Defines the address width for the Caliptra AXI Manager interface, driven by Caliptra's DMA assist block. The address width value assigned to this macro should match the address width that is expected at the integrator's SoC AXI interconnect. |
| CALIPTRA_PRIM_ROOT | environment variable | The integrator needs to set this environment variable to the path to technology specific modules for synthesis. Defaults to $CALIPTRA_ROOT/src/caliptra_prim_generic. |
| CALIPTRA_PRIM_MODULE_PREFIX | environment variable | The integrator needs to set this environment variable to specify the module name prefix for technology specific modules for synthesus. Defaults to 'caliptra_prim_generic'. |
Interface
The following tables describe the interface signals.
Table 4: Clocks and resets
| Signal name | Width | Driver | Synchronous (as viewed from Caliptraβs boundary) | Description |
|---|---|---|---|---|
| cptra_pwrgood | 1 | Input | Asynchronous Assertion Synchronous deassertion to clk | Active high power good indicator. Deassertion hard resets Caliptra. |
| cptra_rst_b | 1 | Input | Asynchronous Assertion Synchronous deassertion to clk | Active low asynchronous reset. |
| clk | 1 | Input | Convergence and validation done at 400MHz. All other frequencies are up to the user. |
Table 5: AXI Subordinate Interface
| Signal name | Width | Driver | Synchronous (as viewed from Caliptraβs boundary) | Description |
|---|---|---|---|---|
| araddr | AW | Input | Synchronous to clk | AR channel address |
| arburst | 2 | Input | Synchronous to clk | AR channel burst encoding |
| arsize | 3 | Input | Synchronous to clk | AR channel size encoding |
| arlen | 8 | Input | Synchronous to clk | AR channel length, beats in the burst |
| aruser | UW | Input | Synchronous to clk | AR channel user signal. Identifies the requester for mailbox and fuse access. See AXI_USER details for more information. |
| arid | IW | Input | Synchronous to clk | AR channel id signal |
| arlock | 1 | Input | Synchronous to clk | AR channel lock signal |
| arvalid | 1 | Input | Synchronous to clk | AR channel valid handshake signal |
| arready | 1 | Output | Synchronous to clk | AR channel ready handshake signal |
| rdata | DW | Output | Synchronous to clk | R channel read response data |
| rresp | 2 | Output | Synchronous to clk | R channel read response encoding |
| rid | IW | Output | Synchronous to clk | R channel read response id signal |
| ruser | UW | Output | Synchronous to clk | R channel read response user signal |
| rlast | 1 | Output | Synchronous to clk | R channel read response last beat signal |
| rvalid | 1 | Output | Synchronous to clk | R channel valid handhsake signal |
| rready | 1 | Input | Synchronous to clk | R channel ready handshake signal |
| awaddr | AW | Input | Synchronous to clk | AW channel address |
| awburst | 2 | Input | Synchronous to clk | AW channel burst encoding |
| awsize | 3 | Input | Synchronous to clk | AW channel size encoding |
| awlen | 8 | Input | Synchronous to clk | AW channel length, beats in the burst |
| awuser | UW | Input | Synchronous to clk | AW channel user signal. Identifies the requester for mailbox and fuse access. See AXI_USER details for more information. |
| awid | IW | Input | Synchronous to clk | AW channel id signal |
| awlock | 1 | Input | Synchronous to clk | AW channel lock signal |
| awvalid | 1 | Input | Synchronous to clk | AW channel valid handhsake signal |
| awready | 1 | Output | Synchronous to clk | AW channel ready handshake signal |
| wdata | DW | Input | Synchronous to clk | W channel write data |
| wuser | UW | Input | Synchronous to clk | W channel write user |
| wstrb | DW/8 | Input | Synchronous to clk | W channel write strobe. Byte enable. |
| wlast | 1 | Input | Synchronous to clk | W channel write last beat signal |
| wvalid | 1 | Input | Synchronous to clk | W channel valid handhsake signal |
| wready | 1 | Output | Synchronous to clk | W channel ready handshake signal |
| bresp | 2 | Output | Synchronous to clk | B channel write response encoding |
| bid | IW | Output | Synchronous to clk | B channel write response id signal |
| buser | UW | Output | Synchronous to clk | B channel write response user signal |
| bvalid | 1 | Output | Synchronous to clk | B channel valid handhsake signal |
| bready | 1 | Input | Synchronous to clk | B channel ready handshake signal |
Table 6: Mailbox notifications
| Signal name | Width | Driver | Synchronous (as viewed from Caliptraβs boundary) | Description |
|---|---|---|---|---|
| ready_for_fuses | 1 | Output | Synchronous to clk | Indicates that Caliptra is ready for fuse programming. |
| ready_for_mb_processing | 1 | Output | Synchronous to clk | Indicates that Caliptra is ready for processing mailbox commands. |
| ready_for_runtime | 1 | Output | Synchronous to clk | Indicates that Caliptra firmware is ready for RT flow. |
| mailbox_data_avail | 1 | Output | Synchronous to clk | Indicates that the mailbox has a response for SoC to read. Signal is set when the mailbox transitions to the EXECUTE_SOC state, which is also reported in the mbox_status register. |
| mailbox_flow_done | 1 | Output | Synchronous to clk | Deprecated output signal. Reflects the value from the CPTRA_FLOW_STATUS register field mailbox_flow_done, which is not used by firmware. For an indicator that Caliptra has completed its processing of the mailbox flow, an SoC may use the mailbox_data_avail signal. |
Table 7: Caliptra SRAM interface
| Signal name | Width | Driver | Synchronous (as viewed from Caliptraβs boundary) | Description |
|---|---|---|---|---|
| mbox_sram_cs | 1 | Output | Synchronous to clk | Chip select for mbox SRAM |
| mbox_sram_we | 1 | Output | Synchronous to clk | Write enable for mbox SRAM |
| mbox_sram_addr | MBOX_ADDR_W | Output | Synchronous to clk | Addr lines for mbox SRAM |
| mbox_sram_wdata | MBOX_DATA_W | Output | Synchronous to clk | Write data for mbox SRAM |
| mbox_sram_rdata | MBOX_DATA_W | Input | Synchronous to clk | Read data for mbox SRAM |
| imem_cs | 1 | Output | Synchronous to clk | Chip select for imem SROM |
| imem_addr | IMEM_ADDR_WIDTH | Output | Synchronous to clk | Addr lines for imem SROM |
| imem_rdata | IMEM_DATA_WIDTH | Input | Synchronous to clk | Read data for imem SROM |
| iccm_clken | ICCM_NUM_BANKS | Input | Synchronous to clk | Per-bank clock enable |
| iccm_wren_bank | ICCM_NUM_BANKS | Input | Synchronous to clk | Per-bank write enable |
| iccm_addr_bank | ICCM_NUM_BANKS x (ICCM_BITS-4) | Input | Synchronous to clk | Per-bank address |
| iccm_bank_wr_data | ICCM_NUM_BANKS x 32 | Input | Synchronous to clk | Per-bank input data |
| iccm_bank_wr_ecc | ICCM_NUM_BANKS x 7 | Input | Synchronous to clk | Per-bank input ecc |
| iccm_bank_dout | ICCM_NUM_BANKS x 32 | Output | Synchronous to clk | Per-bank output data |
| iccm_bank_ecc | ICCM_NUM_BANKS x 7 | Output | Synchronous to clk | Per-bank output ecc |
| dccm_clken | DCCM_NUM_BANKS | Input | Synchronous to clk | Per-bank clock enable |
| dccm_wren_bank | DCCM_NUM_BANKS | Input | Synchronous to clk | Per-bank write enable |
| dccm_addr_bank | DCCM_NUM_BANKS x (DCCM_BITS-4) | Input | Synchronous to clk | Per-bank address |
| dccm_wr_data_bank | DCCM_NUM_BANKS x DCCM_DATA_WIDTH | Input | Synchronous to clk | Per-bank input data |
| dccm_wr_ecc_bank | DCCM_NUM_BANKS x DCCM_ECC_WIDTH | Input | Synchronous to clk | Per-bank input ecc |
| dccm_bank_dout | DCCM_NUM_BANKS x DCCM_DATA_WIDTH | Output | Synchronous to clk | Per-bank output data |
| dccm_bank_ecc | DCCM_NUM_BANKS x DCCM_ECC_WIDTH | Output | Synchronous to clk | Per-bank output ecc |
Table 8: Adams-Bridge SRAM Interface
Adams-Bridge SRAM interface is used to connect the necessary SRAM instances for Adams-Bridge. There are 8 SRAMs, 2 of which have 2 banks. Each SRAM has a parameterized data width and depth used to calculate the addr width.
All memories are modeled as 1 read 1 write port RAMs with a flopped read data. See abr_1r1w_ram.sv and abr_1r1w_be_ram.sv for examples. Strobe width describes the number of bits enabled by each strobe. All strobed memories are byte enabled in the design. See ABR Memory requirement for more details.
The full set of wires is encapsulated in the abr_mem_if construct abr_memory_export at the Caliptra boundary.
The table below details the interface required for each SRAM. Driver direction is from the perspective of Caliptra.
| Signal name | Width | Driver | Synchronous (as viewed from Caliptraβs boundary) | Description |
|---|---|---|---|---|
| we_i | 1 | Output | Synchronous to clk | Write enable |
| waddr_i | ADDR_W | Output | Synchronous to clk | Write address |
| wdata_i | DATA_W | Output | Synchronous to clk | Write data |
| wstrobe_i | DATA_W/8 | Output | Synchronous to clk | Write strobe (only for sig_z and pk memories) |
| re_i | 1 | Output | Synchronous to clk | Read enable |
| raddr_i | ADDR_W | Output | Synchronous to clk | Read address |
| rdata_o | DATA_W | Input | Synchronous to clk | Read data |
Table 9: JTAG interface
| Signal name | Width | Driver | Synchronous (as viewed from Caliptraβs boundary) | Description |
|---|---|---|---|---|
| jtag_tck | 1 | Input | ||
| jtag_tms | 1 | Input | Synchronous to jtag_tck | |
| jtag_tdi | 1 | Input | Synchronous to jtag_tck | |
| jtag_trst_n | 1 | Input | Asynchronous assertion Synchronous deassertion to jtag_tck | |
| jtag_tdo | 1 | Output | Synchronous to jtag_tck |
Table 10: RISC-V Trace interface Trace ports have been directly connected from Caliptra's instance of the VeeR-EL2 RISC-V core to the top-level. However, use of these ports has not been validated. Integrators shall leave these ports unconnected. Support for these ports may be added in a future release.
| Signal name | Width | Driver | Synchronous (as viewed from Caliptraβs boundary) | Description |
|---|---|---|---|---|
| trace_rv_i_insn_ip | 32 | Output | Synchronous to clk | Trace signals from Caliptra RV core instance. Refer to VeeR documentation for more details. |
| trace_rv_i_address_ip | 32 | Output | Synchronous to clk | Trace signals from Caliptra RV core instance. Refer to VeeR documentation for more details. |
| trace_rv_i_valid_ip | 1 | Output | Synchronous to clk | Trace signals from Caliptra RV core instance. Refer to VeeR documentation for more details. |
| trace_rv_i_exception_ip | 1 | Output | Synchronous to clk | Trace signals from Caliptra RV core instance. Refer to VeeR documentation for more details. |
| trace_rv_i_ecause_ip | 5 | Output | Synchronous to clk | Trace signals from Caliptra RV core instance. Refer to VeeR documentation for more details. |
| trace_rv_i_interrupt_ip | 1 | Output | Synchronous to clk | Trace signals from Caliptra RV core instance. Refer to VeeR documentation for more details. |
| trace_rv_i_tval_ip | 32 | Output | Synchronous to clk | Trace signals from Caliptra RV core instance. Refer to VeeR documentation for more details. |
Table 11: Subsystem Straps and Control
| Signal name | Width | Driver | Synchronous (as viewed from Caliptraβs boundary) | Description |
|---|---|---|---|---|
| strap_ss_caliptra_base_addr | 64 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_mci_base_addr | 64 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_recovery_ifc_base_addr | 64 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_external_staging_area_base_addr | 64 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_otp_fc_base_addr | 64 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_uds_seed_base_addr | 64 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_key_release_base_addr | 64 | Input Strap | Synchronous to clk | Used in Subsystem mode only. This is the full destination address for MEK generated via OCP LOCK flow. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_key_release_key_size | 64 | Input Strap | Synchronous to clk | Used in Subsystem mode only. This is the size of MEK generated via OCP LOCK flow. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_prod_debug_unlock_auth_pk_hash_reg_bank_offset | 32 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_num_of_prod_debug_unlock_auth_pk_hashes | 32 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_caliptra_dma_axi_user | 32 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_strap_generic_0 | 32 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_strap_generic_1 | 32 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_strap_generic_2 | 32 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| strap_ss_strap_generic_3 | 32 | Input Strap | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. |
| ss_debug_intent | 1 | Input | Synchronous to clk | Sampled on cold reset. Used in Subsystem mode only. Indicates that the SoC is in debug mode and a user intends to request unlock of debug mode through the TAP mailbox. In Passive mode, integrators shall tie this input to 0. |
| ss_ocp_lock_en | 1 | Input | Synchronous to clk | Sampled on cold reset. Used in Subsystem mode only. Indicates that the SoC enables OCP LOCK features of Caliptra. Must be tied to a constant value. For example, driving this input from a programmable register or from a package pin is not permitted. |
| ss_dbg_manuf_enable | 1 | Output | Synchronous to clk | Enables unlock of the debug interface in the Manufacturing security state, for Subsystem mode only. |
| ss_soc_dbg_unlock_level | 64 | Output | Synchronous to clk | Enables unlock of the debug interface in the Production security state, for Subsystem mode only. |
| ss_generic_fw_exec_ctrl | 128 | Output | Synchronous to clk | Enables SoC processors to execute firmware once authenticated by Caliptra. |
| recovery_data_avail | 1 | Input | Synchronous to clk | Input from streaming boot interface (a.k.a. recovery interface) indicating that a payload is available in the data buffer. |
| recovery_image_activated | 1 | Input | Synchronous to clk | Input from streaming boot interface (a.k.a. recovery interface) indicating that firmware image is activated. |
Table 12: Security and miscellaneous
| Signal name | Width | Driver | Synchronous (as viewed from Caliptraβs boundary) | Description |
|---|---|---|---|---|
| cptra_obf_key | 256 | Input Strap | Asynchronous | Obfuscation key is driven by SoC at integration time. Ideally this occurs just before tape-in and the knowledge of this key must be protected unless PUF is driving this. The key is latched by Caliptra upon first deassertion of the warm reset following caliptra powergood assertion. It is not resampled during subsequent warm resets, therefore the value is only captured upon a cold boot. It is cleared after its use and can only re-latched on a power cycle (powergood deassertion to assertion). |
| cptra_csr_hmac_key | 512 | Input Strap | Asynchronous | CSR HMAC key is driven by SoC at integration time. Ideally this occurs just before tape-in and the knowledge of this key must be protected. The key is latched by Caliptra on caliptra powergood assertion during DEVICE_MANUFACTURING lifecycle state. |
| cptra_obf_field_entropy_vld | 1 | Input | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. Valid signal used to sample cptra_obf_field_entropy if it is driven by wires from the fuse controller. |
| cptra_obf_field_entropy | 256 | Input | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. Fuse controller can optionally drive the field entropy value over wires through this interface. The value is sampled after warm reset if the valid cptra_obf_field_entropy_vld is asserted. |
| cptra_obf_uds_seed_vld | 1 | Input | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. Valid signal used to sample cptra_obf_uds_seed if it is driven by wires from the fuse controller. |
| cptra_obf_uds_seed | 512 | Input | Synchronous to clk | Used in Subsystem mode only. In Passive mode, integrators shall tie this input to 0. Fuse controller can optionally drive the uds seed value over wires through this interface. The value is sampled after warm reset if the valid cptra_obf_uds_seed_vld is asserted. |
| security_state | 3 | Input Strap | Synchronous to clk | Security state that Caliptra should take (for example, manufacturing, secure, unsecure, etc.). The key is latched by Caliptra on cptra_noncore_rst_b deassertion. Any time the state changes to debug mode, all keys, assets, and secrets stored in fuses or key vault are cleared. Cryptography core states are also flushed if they were being used. |
| scan_mode | 1 | Input Strap | Synchronous to clk | Must be set before entering scan mode. This is a separate signal than the scan chain enable signal that goes into scan cells. This allows Caliptra to flush any assets or secrets present in key vault and flops if the transition is happening from a secure state. |
| generic_input_wires | 64 | Input | Synchronous to clk | Placeholder of input wires for late binding features. These values are reflected into registers that are exposed to firmware. |
| generic_output_wires | 64 | Output | Synchronous to clk | Placeholder of output wires for late binding features. Firmware can set the wires appropriately via register writes. |
| cptra_error_fatal | 1 | Output | Synchronous to clk | Indicates a fatal error from Caliptra. |
| cptra_error_non_fatal | 1 | Output | Synchronous to clk | Indicates a non fatal error from Caliptra. |
| BootFSM_BrkPoint | 1 | Input Strap | Asynchronous | Stops the BootFSM to allow TAP writes set up behavior. Examples of these behaviors are skipping or running ROM flows, or stepping through BootFSM. |
| etrng_req | 1 | Output | Synchronous to clk | External source mode: TRNG_REQ to SoC. SoC writes to TRNG architectural registers with a NIST-compliant entropy. Internal source mode: TRNG_REQ to SoC. SoC enables external RNG digital bitstream input into itrng_data/itrng_valid. |
| itrng_data | 4 | Input | Synchronous to clk | External source mode: Not used. Internal source mode only: Physical True Random Noise Source (PTRNG for "Number Generator") digital bit stream from SoC, which is sampled when itrng_valid is high. See the Hardware Specification for details on PTRNG expectations and iTRNG entropy capabilities. |
| itrng_valid | 1 | Input | Synchronous to clk | External source mode: Not used. Internal source mode only: RNG bit valid. This is valid per transaction. itrng_data can be sampled whenever this bit is high. The expected itrng_valid output rate is dependent on the process node technology. For 40nm, it is expected to be at least 50kHz. For latest industry standard, moderately advanced technology, it is expected to be greater than 400kHz. |
Architectural registers and fuses
Control registers and fuses are documented on GitHub.
- External Registers: caliptra_top_reg β caliptra_top_reg Reference (chipsalliance.github.io)
- Internal Registers - clp β clp Reference (chipsalliance.github.io)
Fuses
Fuses may only be written during the BOOT_FUSE state of the Boot FSM and require a cptra_pwrgood to be recycled to be written again.
After all fuses are written, the fuse done register at the end of the fuse address space must be set to 1 to lock the fuse writes and to proceed with the boot flow.
Although fuse values (and the fuse done register) persist across a warm reset, SoC is still required to perform a write to the fuse done register while in the BOOT_FUSE state in order to complete the bringup from reset. See Boot FSM for further details.
Note: Starting in Caliptra 2.0 subsystem strap and other configuration registers have been added to the fuse region. Subsystem straps are expected to be programmed during the same time as fuses per Caliptra spec. The following SS registers in the fuse region are not straps, are intended for internal use in Caliptra, and cannot be written by any SoC agents; therefore, categorizing them under the FUSE range has no effect:
SS_DBG_SERVICE_REG_RSPSS_SOC_DBG_UNLOCK_LEVELSS_GENERIC_FW_EXEC_CTRL
The remaining register SS_DBG_SERVICE_REG_REQ is initialized by the same SoC agent as fuses and is the only agent that can make subsystem debug service requests.
Interface rules
The following figure shows the reset rules and timing for cold boot flows.
Figure: Reset rules and timing diagram

Deassertion of cptra_pwrgood indicates a power cycle that results in returning Caliptra to its default state. All resettable flops are reset.
Deassertion of cptra_rst_b indicates a warm reset cycle that resets all but the βstickyβ registers (fuses, error logging, etc.).
Assertion of BootFSM_BrkPoint stops the boot flow from releasing Caliptra from reset after fuse download. Writing a 1 to the GO field of the CPTRA_BOOTFSM_GO register allows the boot flow to proceed.
AXI
Arbitration
Caliptra has two interfaces attached to the AXI bus: a subordinate, incapable of initiating transfers, and a manager interface. The AXI manager is only enabled in Caliptra Subsystem mode, and must be tied to 0 in all other use-cases. The AXI subordinate is used by SoC agents to interact with the Caliptra external registers. If SoCs have multiple AXI agents or other proprietary-fabric protocols that require any special fabric arbitration, that arbitration is done at SoC level.
AXI User
AXI address user request signals (ARUSER and AWUSER, collectively "AxUSER") are used to uniquely identify AXI agents that have issued the request to Caliptra. Refer to Mailbox AXI User Attribute Register and SoC Integration Requirements for additional details.
Unsupported features
The Caliptra AXI subordinate has the following usage restrictions:
- Single outstanding transaction is serviced at a time (read or write). Operation is half-duplex due to the underlying register access interface.
- AXI read and write requests may be accepted simultaneously by the AXI subordinate, but internal arbitration will service them one at a time.
- Responses are in order
- Burst data interleaving is not supported
- SoC agents shall not initiate AXI burst transfers to the SoC interface, except as write bursts to the mbox_datain register or read bursts from the mbox_dataout register. Such bursts shall be of the AXI "FIXED" burst type.
- Accesses to these registers shall not be "narrow". This means that AxSIZE must be set to 0x2 and WSTRB must be set to 0xF.
- mbox_datain
- mbox_dataout
- CPTRA_TRNG_DATA
- Violations of the AXI specification by AXI managers will result in undefined behavior. Examples include:
- AxSIZE values larger than interface width (greater than 0x2).
- AxLEN larger than legal value (256 maximum burst size, 16 for FIXED bursts, and total burst length must be 4096 Bytes or less).
- Number of data beats on W channel does not match burst length indicated on AWLEN.
- RRESP or BRESP has an undefined value.
- WLAST is driven incorrectly, driven on multiple beats, or never driven.
- Exclusive accesses are not supported. I.e. AxLOCK must be tied to 0.
- The following signals are unused/unconnected:
- AxCACHE
- AxPROT
- AxREGION
- AxQOS
Undefined address accesses
All accesses that are outside of the defined address space of Caliptra are responded to by Caliptraβs SoC interface:
- All reads to undefined addresses get completions with zero data.
- All writes to undefined addresses are dropped.
- All other undefined opcodes are silently dropped.
- Access to mailbox memory region with invalid AXI_USER are dropped.
- Access to a fuse with invalid AXI_USER are dropped.
- Access to the trng with invalid AXI_USER are dropped.
- SLVERR is asserted for any of the above conditions.
All accesses must be 32-bit aligned. Misaligned writes are dropped and reads return 0x0.
DMA Assist Engine
Caliptra contains a DMA assist engine and AXI manager interface that is used in Subsystem mode to initiate AXI transactions to the SoC AXI interconnect. When Caliptra is integrated in passive mode the DMA assist block is not available for use; all AXI manager interfaces must be tied to 0 and must not be connected to the SoC interconnect. For details on the DMA block in Subsystem mode operation, refer to the Caliptra Subsystem Hardware Specification.
Undefined mailbox usages
A trusted/valid requester that locks the mailbox and never releases the lock will cause the mailbox to be locked indefinitely.
Caliptra firmware internally has the capability to force release the mailbox based on various timers but there is no architectural requirement to use this capability.
Straps
Straps are signal inputs to Caliptra that are sampled once on reset exit, and the latched value persists throughout the remaining uptime of the system. Straps are sampled on either cptra_pwrgood signal assertion (cold reset exit) or cptra_noncore_rst_b deassertion (warm reset exit) β refer to interface table for list of straps. In 2.0, Caliptra adds support for numerous Subsystem-level straps. These straps are initialized on warm reset deassertion to the value from the external port, but may also be rewritten by the SoC firmware at any time prior to CPTRA_FUSE_WR_DONE being set. These must be programmed by SoC FW at the same time as Caliptra Fuses per Caliptra Spec. Once written and locked, the values of these straps persist until a cold reset.
Obfuscation key
SoC drives the key at the tape-in time of the SoC using an Engineering Change Order (ECO) and must be protected from common knowledge. For a given SoC construction, this can be driven using a PUF too.
The key must follow the security rules defined in the Caliptra architectural specification.
SoC must ensure that there are no SCAN cells on the flops that latch this key internally to Caliptra.
CSR HMAC key
SoC drives the key at the tape-in time of the SoC using an Engineering Change Order (ECO) and must be protected from common knowledge.
The key must follow the security rules defined in the Caliptra architectural specification.
SoC must ensure that there are no SCAN cells on the flops that latch this key internally to Caliptra.
Late binding interface signals
The interface signals generic_input_wires, generic_output_wires, and strap_ss_strap_generic_N are placeholders on the SoC interface reserved for late binding features. This may include any feature that is required for correct operation of the design in the final integrated SoC and that may not be accommodated through existing interface signaling (such as the mailbox).
While these late binding interface pins are generic in nature until assigned a function, integrators must not define non-standard use cases for these pins. Defining standard use cases ensures that the security posture of Caliptra in the final implementation is not degraded relative to the consortium design intent. Bits in generic_input_wires and strap_ss_strap_generic_N that don't have a function defined in Caliptra must be tied to a 0-value. These undefined input bits shall not be connected to any flip flops (which would allow run-time transitions on the value).
Each wire connects to a register in the SoC Interface register bank through which communication to the internal microprocessor may be facilitated. Each of the generic wire signals is 64 bits in size. The size of the generic strap is indicated in Table 11.
Activity on any bit of the generic_input_wires triggers a notification interrupt to the microcontroller indicating a bit toggle.
The following table describes the allocation of functionality on generic_input_wires. All bits not listed in this table must be tied to 0.
Table 13: generic_input_wires function binding
| Bit | Name | Description |
|---|---|---|
| 0 | Zeroization status | Used by SoC to provide zeroization status of fuses. |
| 63:1 | RESERVED | No allocated function. |
The following table describes the allocation of functionality to strap_ss_strap_generic_N. All straps not listed in this table must be tied to 0.
Table 14: strap_ss_strap_generic_N function binding
| N | Name | Description |
|---|---|---|
| 0 | strap_ss_strap_generic_0 | Provides the Caliptra ROM with a 32-bit pointer that encodes the location of the fuse controller's status register and the bit position of the idle indicator. Upper 16 bits: Bit index of the IDLE_BIT_STATUS within SOC_OTP_CTRL_STATUS. Lower 16 bits: Offset address of SOC_OTP_CTRL_STATUS within the SOC_IFC_REG space, relative to SOC_OTP_CTRL_BASE_ADDR. |
| 1 | strap_ss_strap_generic_1 | Provides the Caliptra ROM with a 32-bit pointer to the fuse controllerβs command register (CMD), enabling ROM-level control or triggering of fuse operations. |
| 2 | RESERVED | No allocated function. |
| 3 | RESERVED | No allocated function. |
SoC interface operation
The Caliptra mailbox is the primary communication method between Caliptra and the SoC that Caliptra is integrated into.
The Caliptra mailbox uses an AXI interface to communicate with the SoC. The SoC can write to and read from various memory mapped register locations over the AXI interface in order to pass information to Caliptra.
Caliptra in turn also uses the mailbox to pass information back to the SoC. The interface does not author any transaction on the AXI interface. The interface only signals to the SoC that data is available in the mailbox and it is the responsibility of the SoC to read that data from the mailbox.
Boot FSM
The Boot FSM detects that the SoC is bringing Caliptra out of reset. Part of this flow involves signaling to the SoC that Caliptra is ready for fuses. After fuses are populated and the SoC indicates that it is done downloading fuses, the boot FSM wakes up the rest of Caliptra by deasserting the internal reset. Refer to CaliptraHardwareSpecification.md for more details and diagrams.
The boot FSM first waits for the SoC to assert cptra_pwrgood and deassert cptra_rst_b. The SoC first provides a stable clock to Caliptra. After a minimum of 10 clock cycles have elapsed on the stable clock, the SoC asserts cptra_pwrgood. The SoC waits for a minimum of 10 clocks after asserting cptra_pwrgood before deasserting cptra_rst_b. In the BOOT_FUSE state, Caliptra signals to the SoC that it is ready for fuses. After the SoC is done writing fuses, it sets the fuse done register and the FSM advances to BOOT_DONE.
BOOT_DONE enables Caliptra reset deassertion through a two flip-flop synchronizer.
SoC access mechanism
The SoC communicates with the mailbox through an AXI Interface. The SoC acts as the requester with the Caliptra mailbox as the receiver.
The AXI_USER bits are used by the SoC to identify which device is accessing the mailbox.
External Staging Area
To save SRAM area when Caliptra operates in Subsystem mode, the mailbox (MBOX) SRAM is reduced to 16 KiB.
Instead of passing images directly to Caliptra through the mailbox, the SoC can configure an external staging SRAM that Caliptra fetches from and processes.
Caliptra Core receives the base address of this staging area through the SOC_IFC register SS_EXTERNAL_STAGING_AREA_BASE_ADDR. The address must be an AXI address accessable via the Caliptra DMA controller. This register is exposed as a strap strap_ss_external_staging_area_base_addr and is overridable by SW until FUSE_DONE is set.
For hitless updates or other image-processing operations, the Caliptra mailbox should be used to:
- Notify Caliptra that an image is available for processing.
- Specify the command to run on the image.
- Indicate the size of the image in the staging area.
References:
The external staging area must be within the Caliptra crypto boundary. Meaning there must be access restrictions similar to the MBOX preventing trusted entities from manipulating or accessing the data being processed by Caliptra.
Mailbox
The Caliptra mailbox is a 256 KiB when in passive mode and 16 KiB when in subsystem mode buffer used for exchanging data between the SoC and the Caliptra microcontroller. See External Staging Area why the MBOX SRAM size is smaller in subsystem mode.
When a mailbox is populated by the SoC, initiation of the operation by writing the execute bit triggers an interrupt to the microcontroller. This interrupt indicates that a command is available in the mailbox. The microcontroller is responsible for reading from and responding to the command.
When a mailbox is populated by the microcontroller, an output wire to the SoC indicates that a command is available in the mailbox. The SoC is responsible for reading from and responding to the command.
Mailboxes are generic data-passing structures with a specific protocol that defines legal operations. This protocol for writing to and reading from the mailbox is enforced in hardware as described in the Caliptra mailbox errors section. How the command and data are interpreted by the microcontroller and SoC are not enforced in this specification.
Sender Protocol
Sending data to the mailbox:
- Requester queries the mailbox by reading the LOCK control register.
- If LOCK returns 0, LOCK is granted and will be set to 1.
- If LOCK returns 1, MBOX is locked for another device.
- Requester writes the command to the COMMAND register.
- Requester writes the data length in bytes to the DLEN register.
- Requester writes data packets to the MBOX DATAIN register.
- Requester writes to the EXECUTE register.
- Requester reads the STATUS register. Status can return:
- CMD_BUSY - 2βb00 β Indicates the requested command is still in progress
- DATA_READY - 2βb01 β Indicates the return data is in the mailbox for requested command
- CMD_COMPLETE- 2βb10 β Indicates the successful completion of the requested command
- CMD_FAILURE- 2βb11 β Indicates the requested command failed
- Requester reads the response if DATA_READY was the status.
- Requester resets the EXECUTE register to release the lock.
Notes on behavior:
Once LOCK is granted, the mailbox is locked until that device has concluded its operation. Caliptra has access to an internal mechanism to terminate a lock early or release the lock if the device does not proceed to use it or to recover from deadlock scenarios. The following figure shows the sender protocol flow.
Figure: Sender protocol flow chart

Receiver Protocol
Upon receiving indication that mailbox has been populated, the appropriate device can read the mailbox. This is indicated by a dedicated wire that is asserted when Caliptra populates the mailbox for SoC consumption.
Caliptra will not initiate any mailbox commands that require a response from the SoC. Caliptra initiated mailbox commands are βbroadcastβ and available to any user on the SoC. SoC will not be able to write the DLEN or DATAIN register while processing a Caliptra initiated mailbox command.
Receiving data from the mailbox:
- On mailbox_data_avail assertion, the receiver reads the COMMAND register.
- Receiver reads the DLEN register.
- Receiver reads the CMD register.
- Receiver reads the MBOX DATAOUT register.
- Continue reading MBOX DATAOUT register until DLEN bytes are read.
- If a response is required, the receiver can populate the mailbox with the response by updating the DLEN register and writing to DATAIN with the response. (NOTE: The new DLEN value will not take effect until control is returned to the sender via write to the status register).
- Set the mailbox status register appropriately to hand control back to the sender.
- The sender will reset the EXECUTE register.
- This releases the LOCK on the mailbox.
The following figure shows the receiver protocol flow.
Figure: Receiver protocol flowchart

TAP mailbox mode
When Caliptra sets the tap_mode register, the mailbox will transition from RDY_FOR_DATA to EXECUTE_TAP instead of EXECUTE_SOC. This will pass control of the mailbox to the TAP. TAP will follow the Receiving data from the mailbox protocol detailed above.
When TAP acquires the mailbox lock, the mailbox will transition from RDY_FOR_DATA_to EXECUTE_UC. This transition results in the assertion of the internal interrupt signal uc_mailbox_data_avail to UC.
This will pass control of the mailbox to the UC. UC will follow the Receiving data from the mailbox protocol detailed above.
Mailbox arbitration
From a mailbox protocol perspective, as long as CPTRA_VALID_AXI_USER registers carry valid requesters, mailbox lock can be obtained by any of those valid requesters but only one of them at any given time. While the mailbox flow is happening, all other requesters will not get a grant.
A request for lock that is denied due to firmware having the lock results in an interrupt to the firmware. Firmware can optionally use this interrupt to release the lock.
There is no fair arbitration scheme between SoC and microcontroller. It is first come, first served. When the mailbox is locked for microcontroller use and SoC has unsuccessfully requested the mailbox (due to mailbox actively being used), the mailbox generates an interrupt to the microcontroller as a notification.
Further, there is no arbitration between various AXI_USER attributes. AXI_USER attributes exist for security and filtering reasons only.
MAILBOX AXI USER attribute register
It is strongly recommended that these AXI_USER registers are either set at integration time through integration parameters or be programmed by the SoC ROM before any mutable firmware or ROM patches are applied.
SoC SHALL not use value 0xFFFFFFFF as a valid AXI user value for any of the below settings. This is reserved for Caliptra-internal usage.
Programmable registers
Caliptra provides 5 programmable registers that SoC can set at boot time to limit access to the mailbox peripheral. The default AXI_USER set by the integration parameter CPTRA_DEF_MBOX_VALID_AXI_USER is valid at all times. CPTRA_MBOX_VALID_AXI_USER registers become valid once the corresponding lock bit CPTRA_MBOX_AXI_USER_LOCK is set.
Table 15: AXI_USER register definition
| Register | Description |
|---|---|
| CPTRA_MBOX_VALID_AXI_USER[4:0][31:0] | 5 registers for programming AXI_USER values that are considered valid for accessing the mailbox protocol. Requests with AXI_USER attributes that are not in this list will be ignored. |
| CPTRA_MBOX_AXI_USER_LOCK[4:0] | 5 registers, bit 0 of each will lock and mark VALID for the corresponding VALID_AXI_USER register. |
Parameter override
Another option for limiting access to the mailbox peripheral are the integration time parameters that override the programmable AXI_USER registers. At integration time, the CPTRA_SET_MBOX_AXI_USER_INTEG parameters can be set to 1 which enables the corresponding CPTRA_MBOX_VALID_AXI_USER parameters to override the programmable register.
Table 16: AXI_USER Parameter definition
| Parameter | Description |
|---|---|
| CPTRA_SET_MBOX_AXI_USER_INTEG[4:0] | Setting to 1 enables the corresponding CPTRA_MBOX_VALID_AXI_USER parameter. |
| CPTRA_MBOX_VALID_AXI_USER[4:0] | Value to override programmable AXI_USER register at integration time if corresponding CPTRA_SET_MBOX_AXI_USER_INTEG parameter is set to 1. |
Caliptra mailbox protocol
After the SoC side has written the EXECUTE register, the mailbox sends an interrupt to the microcontroller.
The microcontroller reads the COMMAND and DLEN registers, as well as the data populated in the mailbox.
The microcontroller can signal back to SoC through functional registers, and populate COMMAND, DLEN, and MAILBOX as well.
Caliptra mailbox errors
Mailbox is responsible for only accepting writes from the device that requested and locked the mailbox.
If the SoC violates this protocol, the mailbox flags a protocol violation and enters an error state. Two protocol violations are detected:
- Access without lock: Writes to any mailbox register by SoC or reads from the dataout register, without having first acquired the lock, are a violation.
- If any agent currently has the lock, accesses by agents other than the one holding the lock are ignored.
- If no agent currently has the lock, the violation results in a flagged error.
- Out of order access: SoC must follow the rules for the sender and receiver protocol that define access ordering and progression for a mailbox command.
- If, after acquiring the lock, an SoC agent performs any register write (or read from the dataout register) outside of the prescribed ordering, this is a flagged violation.
- Such access by any SoC agent that does not have the lock is ignored.
After a mailbox protocol violation is flagged, it is reported to the system in several ways:
-
The mailbox FSM enters the ERROR state in response to an out of order access violation, and the new FSM state is readable via the mailbox status register. The LOCK value is preserved on entry to the ERROR state. The access without lock violation does not result in a state change. After entering the ERROR state, the mailbox may only be restored to the IDLE state by:
- System reset
- Write to the force unlock register by firmware inside Caliptra (via internal bus)
Either of these mechanisms will also clear the mailbox LOCK.
-
Mailbox protocol violations are reported as fields in the HW ERROR non-fatal register. These events also cause assertion of the cptra_error_non_fatal interrupt signal to SoC. Upon detection, SoC may acknowledge the error by clearing the error field in this register via bus write.
-
Mailbox protocol violations generate an internal interrupt to the Caliptra microcontroller. Caliptra firmware is aware of the protocol violation.
The following table describes AXI transactions that cause the Mailbox FSM to enter the ERROR state, given that the register βmbox_userβ contains the value of the AXI USER that was used to originally acquire the mailbox lock.
Table 17: Mailbox protocol error trigger conditions
| FSM state | SoC HAS LOCK | AXI USER eq mbox_user | Error state trigger condition |
|---|---|---|---|
| MBOX_RDY_FOR_CMD | 1 | true | Read from mbox_dataout. Write to any register other than mbox_cmd. |
| MBOX_RDY_FOR_CMD | 1 | false | - |
| MBOX_RDY_FOR_CMD | 0 | - | - |
| MBOX_RDY_FOR_DLEN | 1 | true | Read from mbox_dataout. Write to any register other than mbox_dlen. |
| MBOX_RDY_FOR_DLEN | 1 | false | - |
| MBOX_RDY_FOR_DLEN | 0 | - | - |
| MBOX_RDY_FOR_DATA | 1 | true | Read from mbox_dataout. Write to any register other than mbox_datain or mbox_execute. |
| MBOX_RDY_FOR_DATA | 1 | false | - |
| MBOX_RDY_FOR_DATA | 0 | - | - |
| MBOX_EXECUTE_UC | 1 | true | Read from mbox_dataout. Write to any register. |
| MBOX_EXECUTE_UC | 1 | false | - |
| MBOX_EXECUTE_UC | 0 | - | - |
| MBOX_EXECUTE_SOC | 1 | true | Write to any register other than mbox_execute. |
| MBOX_EXECUTE_SOC | 1 | false | - |
| MBOX_EXECUTE_SOC | 0 | true/false* | Write to any register other than mbox_status. |
* mbox_user value is not used when Caliptra has lock and is sending a Caliptra to SoC mailbox operation.
SoC SHA acceleration block
Overview
The SHA acceleration block is in the SoC interface. The SoC can access the acceleratorβs hardware API and stream data to be hashed over the AXI interface.
SHA acceleration block uses a similar protocol to the mailbox, but has its own dedicated registers. Caliptra is the only permitted user of SHA acceleration, either in streaming mode (via AXI) or in mailbox mode. Use of the SHA acceleration block over AXI is only available in Caliptra Subsystem, and is only available to Caliptra, which will access it via the AXI DMA block. The SHA accelerator checks the AXI AxUSER signal to block any access that originates from an agent other than Caliptra's AXI DMA.
SHA_LOCK register is set on read. A read of 0 indicates the SHA was unlocked and will now be locked for the requesting user.
SHA_MODE register sets the mode of operation for the SHA.
See the Hardware specification for additional details.
- 2βb00 - SHA384 streaming mode
- 2βb01 - SHA512 streaming mode
- 2βb10 - SHA384 mailbox mode (Caliptra only, invalid for SoC requests)
- 2βb11 - SHA512 mailbox mode (Caliptra only, invalid for SoC requests)
SoC Sender Protocol
Sending data to the SHA accelerator:
- Requester queries the accelerator by reading the SHA_LOCK control register.
- If SHA_LOCK returns 0, SHA_LOCK is granted and is set to 1.
- If SHA_LOCK returns 1, it is locked for another device.
- Requester writes the SHA_MODE register to the appropriate mode of operation.
- Requester writes the data length in bytes to the SHA_DLEN register.
- Requester writes data packets to the SHA_DATAIN register until SHA_DLEN bytes are written.
- Requester writes the SHA_EXECUTE register, this indicates that it is done streaming data.
- Requesters can poll the SHA_STATUS register for the VALID field to be asserted.
- Once VALID is asserted, the completed hash can be read from the SHA_DIGEST register.
- Requester must write 1 to the LOCK register to release the lock.
TRNG REQ HW API
For SoCs that choose to not instantiate Caliptraβs internal TRNG, we provide a TRNQ REQ HW API.
While the use of this API is convenient for early enablement, the current Caliptra hardware is unable to provide the same security guarantees with an external TRNG. In particular, it is highly advisable to instantiate an internal TRNG if ROM glitch protection is important.
- Caliptra asserts TRNG_REQ wire (this may be because Caliptraβs internal hardware or firmware made the request for a TRNG).
- SoC writes the TRNG architectural registers.
- SoC write a done bit in the TRNG architectural registers.
- Caliptra deasserts TRNG_REQ.
Having an interface that is separate from the SoC mailbox ensures that this request is not intercepted by any SoC firmware agents (which communicate with SoC mailbox). It is a requirement for FIPS compliance that this TRNG HW API is always handled by SoC hardware gasket logic (and not some SoC ROM or firmware code).
TRNG DATA register is tied to TRNG VALID AXI USER. SoC can program the TRNG VALID AXI USER and lock the register using TRNG_AXI_USER_LOCK[LOCK]. This ensures that TRNG DATA register is read-writeable by only the AXI USER programmed into the TRNG_VALID_AXI_USER register. If the CPTRA_TNRG_AXI_USER_LOCK.LOCK is set to β0, then any agent can write to the TRNG DATA register. If the lock is set, only an agent with a specific TRNG_VALID_AXI_USER can write.
The ROM and firmware currently time out on the TRNG interface after 250,000 attempts to read a DONE bit. This bit is set in the architectural registers, as referenced in 3 in the preceding list.
Internal TRNG
TRNG self-test ROM configuration
The internal TRNG is configured by the ROM to extract entropy used to initialize Control Flow Integrity (CFI) countermeasures. Since the ROM does not use entropy for any cryptographic operations, the TRNG self-tests are not configured for FIPS compliance, but rather to ensure that the quality of the entropy output is sufficient for ROM operation.
The default self-test parameters are provided to the ROM via the
CPTRA_iTRNG_ENTROPY_CONFIG0 and CPTRA_iTRNG_ENTROPY_CONFIG1 registers.
The ROM configures self tests with the following parameters.
Adaptive test
The adaptive self-test thresholds are configured as follows if the high and low
thresholds provided in the CPTRA_iTRNG_ENTROPY_CONFIG0 are non-zero.
entropy_src.ADAPTP_HI_THRESHOLDS.FIPS_THRESH = CPTRA_iTRNG_ENTROPY_CONFIG0.HIGH_THRESHOLD
entropy_src.ADAPTP_LO_THRESHOLDS.FIPS_THRESH = CPTRA_iTRNG_ENTROPY_CONFIG0.HIGH_THRESHOLD
Otherwise, the ROM will use 75% and 25% of the FIPS window size for the default high and low thresholds.
W = 2048 (bits)
entropy_src.ADAPTP_HI_THRESHOLDS.FIPS_THRESH = \(3 * (W / 4)\) = 1536
entropy_src.ADAPTP_LO_THRESHOLDS.FIPS_THRESH = \(W / 4\) = 512
It is strongly recommended to avoid using the default values.
Repetition count test
Caliptra supports two implementations of the repetition count test, one that counts repetitions per physical noise source (REPCNT); and, another that counts repetitions at the symbol level (REPCNTS). The ROM configures the REPCNT version.
The self-test is configured as follows if the CPTRA_iTRNG_ENTROPY_CONFIG1
register is not zero.
entropy_src.REPCNT_THRESHOLDS.FIPS_THRESH = CPTRA_iTRNG_ENTROPY_CONFIG1.REPETITION_COUNT
Otherwise, the ROM will use a default value configuration:
entropy_src.REPCNT_THRESHOLDS.FIPS_THRESH = 41
It is strongly recommended to avoid using the default values.
Recommended TRNG self-test thresholds
The thresholds should be tuned to match the entropy estimate of the noise source (H), which is calculated by applying a NIST-approved entropy estimate calculation against raw entropy extracted from the target silicon.
Important: It is important to note that the TRNG will discard samples that do not pass any of the health tests. Since there is a compression function requiring 2048 bits of good entropy to produce a 384 bit seed, the ROM may stall if the self-test thresholds are too aggressive or if the values are misconfigured. To avoid boot stall issues, it is strongly recommended to characterize the noise source on target silicon and select reliable test parameters. The ROM only needs to provide sufficient entropy for countermeasures, so FIPS-level checks can be performed later, in a less boot-timing-sensitive stage.
The following sections illustrate the self-test parameter configuration. The
entropy_src block provides additional tests, but Caliptra's ROM focuses
primarily on the adaptive and repetition count (REPCNT) tests. All other tests
are left with their reset value configuration, which is equivalent to running
the test with the most permissive settings.
Test parameters
The variable names are as defined in NIST SP 800-90B.
\(Ξ± = 2^{-40}\) (recommended)
\(H = 0.5\) (example, implementation specific)
\(W = 2048\) (constant in ROM/hw)
Adaptive proportion test
The test is configured with to sum all the bits per symbol, due to
entropy_src.CONF.THRESHOLD_SCOPE being enabled. The test essentially treats
the combined input as a single binary stream, counting the occurrences of '1's.
Note: The
critbinomfunction (critical binomial distribution function) is implemented by most spreadsheet applications.
CPTRA_iTRNG_ENTROPY_CONFIG0.high_threshold = \(1 + critbinom(W, 2^{-H}, 1 - Ξ±)\)
CPTRA_iTRNG_ENTROPY_CONFIG0.high_threshold = \(1 + critbinom(2048, 2^{-H}, 1 - 2^{-40})\)
CPTRA_iTRNG_ENTROPY_CONFIG0.high_threshold = 1591
CPTRA_iTRNG_ENTROPY_CONFIG0.low_threshold = W - CPTRA_iTRNG_ENTROPY_CONFIG0.high_threshold
CPTRA_iTRNG_ENTROPY_CONFIG0.low_threshold = 2048 - CPTRA_iTRNG_ENTROPY_CONFIG0.high_threshold
CPTRA_iTRNG_ENTROPY_CONFIG0.low_threshold = 457
Repetition count threshold
The repetition count test as configured in the ROM makes no FIPS compliance claims due to the fact that counts are aggregated for each individual bit. This results in a less restrictive threshold as the test will wait for 4x more repetitions before failing. From an entropy quality perspective, this is deemed acceptable for the current Caliptra release.
\[ \begin{aligned} & RcThresh = \frac{-log_2(Ξ±)}{H} + 1 \ & RcThresh = \frac{40}{H} + 1 \ & RcThresh = 81 \end{aligned} \]
CPTRA_iTRNG_ENTROPY_CONFIG1.repetition_count = RcThresh = 81
FIPS Compliance
Caliptra 1.x and 2.0 do not make any FIPS conformance claims on the self-tests configured by the ROM and executed by the internal TRNG. This is due to the test configuration. See previous sections for more details.
SRAM implementation
Overview
SRAMs are instantiated at the SoC level. Caliptra provides the interface to export SRAMs from internal components.
SRAM repair logic (for example, BIST) and its associated fuses, which are proprietary to companies and their methodologies, is implemented external to the Caliptra boundary.
SRAMs must NOT go through BIST or repair flows across a βwarm resetβ. SoC shall perform SRAM repair during a powergood cycling event ("cold reset") and only prior to deasserting cptra_rst_b. During powergood cycling events, SoC shall also initialize all entries in the SRAM to a 0 value, prior to deasserting caliptra_rst_b.
Mailbox SRAM is implemented with ECC protection. Data width for the mailbox is 32-bits, with 7 parity bits for a Hamming-based SECDED (single-bit error correction and double-bit error detection).
RISC-V internal memory export
To support synthesis flexibility and ease memory integration to various fabrication processes, all SRAM blocks inside the RISC-V core are exported to an external location in the testbench. A single unified interface connects these memory blocks to their parent logic within the RISC-V core. Any memory implementation may be used to provide SRAM functionality in the external location in the testbench, provided the implementation adheres to the interface requirements connected to control logic inside the processor. Memories behind the interface are expected to be implemented as multiple banks of SRAM, from which the RISC-V processor selects the target using an enable vector. The I-Cache has multiple ways, each containing multiple banks of memory, but I-Cache is disabled in Caliptra and this may be removed for synthesis.
The following memories are exported:
- Instruction Closely-Coupled Memory (ICCM)
- Data Closely Coupled Memory (DCCM)
Table 7 indicates the signals contained in the memory interface. Direction is relative to the exported memory wrapper that is instantiated outside of the Caliptra subsystem (that is, from the testbench perspective).
SRAM timing behavior
- [Writes] Input wren signal is asserted simultaneously with input data and address. Input data is stored at the input address 1 clock cycle later.
- [Reads] Input clock enable signal is asserted simultaneously with input address. Output data is available 1 clock cycle later from a flip-flop register stage.
- [Writes] Input wren signal is asserted simultaneously with input data and address. Data is stored at the input address 1 clock cycle later.
The following figure shows the SRAM interface timing.
Figure: SRAM interface timing

SRAM parameterization
Parameterization for ICCM/DCCM memories is derived from the configuration of the VeeR RISC-V core that has been selected for Caliptra integration. Parameters defined in the VeeR core determine signal dimensions at the Caliptra top-level interface and drive requirements for SRAM layout. For details about interface parameterization, see the Interface section. Complete configuration parameters of the RISC-V Core may be found in common_defines.sv. The following table explains some parameters that are used to derive interface signal widths for exported RISC-V SRAM signals.
Table 18: SRAM parameterization
| Parameter | Description |
|---|---|
| ICCM_ENABLE | Configures ICCM to be present in VeeR core. |
| ICCM_NUM_BANKS | Determines the number of physical 39-bit (32-bit data + 7-bit ECC) SRAM blocks that are instantiated in the ICCM. |
| ICCM_INDEX_BITS | Address bit width for each ICCM Bank that is instantiated. |
| ICCM_SIZE | Capacity of the ICCM in KiB. Total ICCM capacity in bytes is given by 4 * ICCM_NUM_BANKS * 2ICCM_INDEX_BITS. |
| DCCM_ENABLE | Configures DCCM to be present in VeeR core. |
| DCCM_NUM_BANKS | Determines the number of physical 39-bit (32-bit data + 7-bit ECC) SRAM blocks that are instantiated in the DCCM. |
| DCCM_INDEX_BITS | Address bit width for each DCCM Bank that is instantiated. |
| DCCM_SIZE | Capacity of the DCCM in KiB. Total DCCM capacity in bytes is given by 4 * DCCM_NUM_BANKS * 2DCCM_INDEX_BITS. |
Example SRAM machine check reliability integration
This section describes an example implementation of integrator machine check reliability.
This example is applicable to scenarios where an integrator may need control of or visibility into SRAM errors for purposes of reliability or functional safety. In such cases, integrators may introduce additional layers of error injection, detection, and correction logic surrounding SRAMs. The addition of such logic is transparent to the correct function of Caliptra, and removes integrator dependency on Caliptra for error logging or injection.
Note that the example assumes that data and ECC codes are in non-deterministic bit-position in the exposed SRAM interface bus. Accordingly, redundant correction coding is shown in the integrator level logic (i.e., integrator_ecc(caliptra_data, caliptra_ecc)). If the Caliptra data and ECC are deterministically separable at the Caliptra interface, the integrator would have discretion to store the ECC codes directly and calculate integrator ECC codes for the data alone.
Figure: Example machine check reliability implementation

Error detection and logging
- Caliptra IP shall interface to ECC protected memories.
- Caliptra IP calculates and applies its own ECC code, which produces a total of 39-bit data written to external or INTEGRATOR instantiated SRAMs.
- Each 39-bit bank memory internally calculates 8-bit ECC on a write and stores 47 bits of data with ECC into SRAM.
- On read access syndrome is calculated based on 39-bit data.
- If parity error is detected and syndrome is valid, then the error is deemed single-bit and correctable.
- If no parity error is detected but syndrome == 0 or the syndrome is invalid, the error is deemed uncorrectable.
- On both single and double errors, the read data is modified before being returned to Caliptra.
- Since single-bit errors shall be corrected through INTEGRATOR instantiated logic, Caliptra never sees single-bit errors from SRAM.
- Double-bit or uncorrectable errors would cause unpredictable data to be returned to Caliptra. Since this condition shall be detected and reported to MCRIP, there is no expectation that Caliptra will operate correctly after a double error.
- On detection, single errors are reported as transparent to MCRIP, double errors are reported as fatal.
- Along with error severity, MCRIP logs physical location of the error.
- After MCRIP logs an error, it has a choice to send out in-band notification to an external agent.
- MCRIP logs can be queried by SoC software.
Error injection
- MCRIP supports two error injection modes: intrusive and non-intrusive.
- Intrusive error injection:
- Can force a single or double error to be injected, which would result in incorrect data to be returned on read access.
- The intrusive error injection mode is disabled in Production fused parts via Security State signal.
- Non-intrusive error injection:
- Allows external software to write into MCRIP error log registers.
- The non-intrusive error injection does not interfere with the operation of memories.
- The non-intrusive error injection is functional in Production fused parts.
Caliptra error handling flow
- Any implementation of error and recovery flows must adhere to the error handling requirements specified in Caliptra.md
- SoC level reporting and handling of fatal & non-fatal errors is product-specific architecture, outside the scope of Caliptra core definition. For example, a CPU and a PCIe device may handle fatal and non-fatal errors differently.
SoC integration requirements
The following table describes SoC integration requirements.
For additional information, see Caliptra assets and threats.
Table 19: SoC integration requirements
| Category | Requirement | Definition of done | Rationale |
|---|---|---|---|
| Obfuscation Key | SoC backend flows shall generate obfuscation key with appropriate NIST compliance as dictated in the Caliptra RoT specification. | Statement of conformance | Required by UDS and Field Entropy threat model |
| Obfuscation Key | If not driven through PUF, SoC backend flows shall ECO the obfuscation key before tapeout. | Statement of conformance | Required by UDS and Field Entropy threat model |
| Obfuscation Key | Rotation of the obfuscation key (if not driven through PUF) between silicon steppings of a given product (for example, A0 vs. B0 vs. PRQ stepping) is dependent on company-specific policies. | Statement of conformance | Required by UDS and Field Entropy threat model |
| Obfuscation Key | SoC backend flows should not insert obfuscation key flops into the scan chain. | Synthesis report | Required by UDS and Field Entropy threat model |
| Obfuscation Key | For defense in depth, it is strongly recommended that debofuscation key flops are not on the scan chain. Remove the following signals from the scan chain: cptra_scan_mode_Latched_d cptra_scan_mode_Latched_f field_storage.internal_obf_key | Statement of conformance | Caliptra HW threat model |
| Obfuscation Key | SoC shall ensure that obfuscation key is available (and wires are stable) before Caliptra reset is de-asserted. | Statement of conformance | Functionality and security |
| Obfuscation Key | SoC shall implement protections for obfuscation key generation logic and protect against debug/sw/scandump visibility. 1. Any flops outside of Caliptra that store obfuscation key or parts of the key should be excluded from scandump. 2. SoC shall ensure that the obfuscation key is sent only to Caliptra through HW wires, and it is not visible anywhere outside of Caliptra. | Statement of conformance | Required for Caliptra threat model |
| CSR HMAC Key | SoC backend flows shall generate CSR signing key with appropriate NIST compliance as dictated in the Caliptra ROT specification. | Statement of conformance | Required by Caliptra threat model |
| CSR HMAC Key | SoC backend flows shall ECO the CSR signing key before tapeout. | Statement of conformance | Required by Caliptra threat model |
| CSR HMAC Key | SoC backend flows should rotate CSR signing key for each project. | Statement of conformance | Required by Caliptra threat model |
| CSR HMAC Key | SoC backend flows should not insert CSR signing key flops into the scan chain. | Statement of conformance | Required by Caliptra threat model |
| DFT | Before scan is enabled (separate signal that SoC implements on scan insertion), SoC shall set Caliptra's scan_mode indication to '1 for 5,000 clocks to allow secrets/assets to be flushed. | Statement of conformance | Required by Caliptra threat model |
| DFT | Caliptraβs TAP should be a TAP endpoint. | Statement of conformance | Functional requirement |
| DFD | Integrators shall not connect Caliptra's exposed RISC-V trace ports to any SoC logic. These ports are unvalidated and any implications to the SoC logic due to connecting these signals have not been analyzed. | Synthesis report | Required for Caliptra threat model |
| Mailbox | SoC shall provide an access path between the mailbox and the application CPU complex on SoCs with such complexes (for example, Host CPUs and Smart NICs). See the Sender Protocol section for details about error conditions. | Statement of conformance | Required for Project Kirkland and TDISP TSM |
| Fuses | SoC shall burn non-field fuses during manufacturing. Required vs. optional fuses are listed in the architectural specification. | Test on silicon | Required for UDS threat model |
| Fuses | SoC shall expose an interface for burning field fuses. Protection of this interface is the SoC vendorβs responsibility. | Test on silicon | Required for Field Entropy |
| Fuses | SoC shall write fuse registers and fuse done via immutable logic or ROM code. | Statement of conformance | Required for Caliptra threat model |
| Fuses | SoC shall expose an API for programming Field Entropy as described in the architecture documentation. SoC shall ensure that Field Entropy can only be programmed via this API and shall explicitly prohibit burning of discrete Field Entropy bits and re-burning of already burned Field Entropy entries. | Test on silicon | Required for Field Entropy |
| Fuses | SoC shall ensure that any debug read paths for fuses are disabled in PRODUCTION lifecycle state. | Test on silicon | Required for Field Entropy |
| Fuses | SoC shall ensure that UDS_SEED and Field Entropy supplied to Caliptra come directly from OTP fuses and there are no debug paths to inject new values. | Statement of conformance | Required for Caliptra threat model |
| Fuses | SoC shall add integrity checks for Caliptra fuses as per SoC policy. | Statement of conformance | Reliability |
| Fuses | SoC should apply shielding/obfuscation measures to protect fuse macro. | Statement of conformance | Required for Caliptra threat model |
| Fuses | SoCs that intend to undergo FIPS 140-3 zeroization shall expose zeroization API as described in zeroization requirements in architecture specification. SoC shall apply appropriate authentication for this API to protect against denial of service and side channel attacks. | Test on silicon | FIPS 140-3 certification |
| Security State | SoC shall drive security state wires in accordance with the SoC's security state. | Statement of conformance | Required for Caliptra threat model |
| Security State | If SoC is under debug, then SoC shall drive debug security state to Caliptra. | Statement of conformance | Required for Caliptra threat model |
| Resets and Clocks | SoC shall start input clock before cptra_pwrgood assertion. The clock must operate for a minimum of 10 clock cycles before SoC asserts cptra_pwrgood. | Statement of conformance | Functional |
| Resets and Clocks | After asserting cptra_pwrgood, SoC shall wait for a minimum of 10 clock cycles before deasserting cptra_rst_b. | Statement of conformance | Functional |
| Resets and Clocks | SoC reset logic shall assume reset assertions are asynchronous and deassertions are synchronous. | Statement of conformance | Functional |
| Resets and Clocks | SoC shall ensure Caliptra's powergood is tied to SoCβs own powergood or any other reset that triggers SoCβs cold boot flow. | Statement of conformance | Required for Caliptra threat model |
| Resets and Clocks | SoC shall ensure Caliptra clock is derived from an on-die oscillator circuit. This is to protect against clock fault injection or clock stretching attacks. | Statement of conformance | Required for Caliptra threat model |
| Resets and Clocks | SoC shall ensure that any programmable Caliptra clock controls are restricted to the SoC Manager. | Statement of conformance | Required for Caliptra threat model |
| Resets and Clocks | SoC should defend against external clock stop attacks. | Statement of conformance | Required for Caliptra threat model |
| Resets and Clocks | SoC should defend against external clock glitching attacks. | Statement of conformance | Required for Caliptra threat model |
| Resets and Clocks | SoC should defend against external clock overclocking attacks. | Statement of conformance | Required for Caliptra threat model |
| TRNG | SoC shall either provision Caliptra with a dedicated TRNG or shared TRNG. It is highly recommended to use dedicated ITRNG | Statement of conformance | Required for Caliptra threat model and Functional |
| TRNG | SoC shall provision the Caliptra embedded TRNG with an entropy source if that is used (vs. SoC-shared TRNG API support). | Statement of conformance | Functional |
| TRNG | If the TRNG is shared, then upon TRNG_REQ, SoC shall use immutable logic or code to program Caliptra's TRNG registers. | Statement of conformance | Required for Caliptra threat model and Functional |
| SRAMs | SoC shall ensure timing convergence with 1-cycle read path for SRAMs. | Synthesis report | Functional |
| SRAMs | SoC shall size SRAMs to account for SECDED. Exception for Adams-Bridge SRAMs that do not utilize SECDED. | Statement of conformance | Functional |
| SRAMs | SoC shall write-protect fuses that characterize the SRAM. | Statement of conformance | Required for Caliptra threat model |
| SRAMs | SoC shall ensure SRAM content is only destroyed on powergood cycling. | Statement of conformance | Functional (Warm Reset, Hitless Update) |
| SRAMs | SoC shall only perform SRAM repair on powergood events and prior to caliptra_rst_b deassertion. SoC shall also ensure that SRAMs are initialized with all 0 data during powergood events, and prior to caliptra_rst_b deassertion. | Statement of conformance | Functional (Warm Reset, Hitless Update) |
| Backend convergence | Caliptra supports frequencies up to 400MHz using an industry standard, moderately advanced technology node as of 2023 September. | Statement of conformance | Functional |
| Power saving | Caliptra clock gating shall be controlled by Caliptra firmware alone. SoC is provided a global clock gating enable signal (and a register) to control. | Statement of conformance | Required for Caliptra threat model |
| Power saving | SoC shall not power-gate Caliptra independently of the entire SoC. | Statement of conformance | Required for Caliptra threat model |
| AXI USER | SoC shall drive AXI USER input for all AXI requests that have a route to Caliptra (e.g. through the AXI interconnect). CANNOT use value 0xFFFFFFFF. This is reserved for Caliptra-internal usage. | Statement of conformance | Required for Caliptra threat model |
| AXI USER | Assigned AXI USER values must be unique for each agent on the interconnect. All SoC AXI agents that have an access path to Caliptra AXI subordinate (or any Caliptra Subsystem components, when using the Subsystem mode) via AXI must either (a) generate AXI transactions using an AXI_USER value that is unique from that used by all other AXI agents on the interconnect or (b) generate AXI transactions using an AXI_USER value that will never overlap with the configured valid AXI users, if the agent is not a valid mailbox user, valid fuse user, or valid trng user. For example, if any AXI agents utilize the AxUSER field for any type of dynamic metadata and are on the same AXI interconnect as Caliptra, all possible AxUSER values from those agents should be avoided for assigning Caliptra VALID AXI USER values. Because AXI AxUSER signals are used to identify accessing agents and enforce access rules, this rule ensures that no single agent may ever generate an AXI transaction that identifies it as originating from a different agent. | Statement of conformance | Required for Caliptra threat model |
| Error reporting | SoC shall report Caliptra error outputs. | Statement of conformance | Telemetry and monitoring |
| Error reporting | SoC shall only recover Caliptra fatal errors via SoC power-good reset. | Statement of conformance | Required for Caliptra threat model |
| TRNG AXI USER Programming rules | If SoC doesnβt program the CPTRA_TRNG_AXI_USER_LOCK[LOCK] and Caliptra is configured in external TRNG mode, then Caliptra HW will accept TRNG data from any SoC entity. | Security | Required for Caliptra threat model |
| TRNG AXI USER Programming rules | If SoC programs CPTRA_TRNG_VALID_AXI_USER and sets CPTRA_TRNG_AXI_USER_LOCK[LOCK] and Caliptra is configured in external TRNG mode, then Caliptra HW will accept TRNG data only from the entity that is programmed into the AXI USER register. | Security | Required for Caliptra threat model |
| TRNG AXI USER Programming rules | It is strongly recommended that these AXI USER registers are either set at integration time through integration parameters or be programmed by the SoC ROM before any mutable FW or ROM patches are absorbed. | Security | Required for Caliptra threat model |
| TRNG AXI USER Programming rules | It is strongly recommended that integrators set the TRNG valid AXI_USER to a non-zero value, due to the above uniqueness requirement. | Security | Required for Caliptra threat model |
| MAILBOX AXI USER programming rules | 5 AXI USER attribute registers are implemented at SoC interface. | Security | Required for Caliptra threat model |
| MAILBOX AXI USER programming rules | At boot time, a default SoC or AXI USER can access the mailbox. The value of this AXI USER is an integration parameter, CPTRA_DEF_MBOX_VALID_AXI_USER. | Security | Required for Caliptra threat model |
| MAILBOX AXI USER programming rules | The value of CPTRA_MBOX_VALID_AXI_USER[4:0] register can be programmed by SoC. After it is locked, it becomes a valid AXI USER for accessing the mailbox. | Security | Required for Caliptra threat model |
| MAILBOX AXI USER programming rules | CPTRA_SET_MBOX_AXI_USER_INTEG parameter can be set along with the corresponding CPTRA_MBOX_VALID_AXI_USER parameter at integration time. If set, these integration parameters take precedence over the CPTRA_MBOX_VALID_AXI_USER[4:0] register. | Security | Required for Caliptra threat model |
| MAILBOX AXI USER programming rules | SoC logic (ROM, HW) that is using the Caliptra mailbox right out of cold reset, without first configuring the programmable mailbox AXI USER registers, must send the mailbox accesses with the default AXI USER, CPTRA_DEF_MBOX_VALID_AXI_USER. | Security | Required for Caliptra threat model |
| MAILBOX AXI USER programming rules | For CPTRA_MBOX_VALID_AXI_USER[4:0], the corresponding lock bits MUST be programmed to β1. This enables the mailbox to accept transactions from non-default AXI USERS. | Security | Required for Caliptra threat model |
| MAILBOX AXI USER programming rules | It is strongly recommended that mailbox AXI USER registers are either set at integration time through integration parameters or are programmed by the SoC ROM before any mutable FW or ROM patches are applied. | Security | Required for Caliptra threat model |
| MAILBOX AXI USER Programming rules | It is strongly recommended that integrators set the MAILBOX valid AXI_USER to non-zero values, due to the above uniqueness requirement. | Security | Required for Caliptra threat model |
| FUSE AXI USER programming rules | 1 AXI USER attribute register is implemented at SoC interface: CPTRA_FUSE_VALID_AXI_USER. | Security | Required for Caliptra threat model |
| FUSE AXI USER programming rules | CPTRA_FUSE_AXI_USER_LOCK locks the programmable valid axi user register, and marks the programmed value as valid. | Security | Required for Caliptra threat model |
| FUSE AXI USER programming rules | Integrators can choose to harden the valid axi user for fuse access by setting the integration parameter, CPTRA_FUSE_VALID_AXI_USER, to the desired value in RTL, and by setting CPTRA_SET_FUSE_AXI_USER_INTEG to 1. If set, these integration parameters take precedence over the CPTRA_FUSE_VALID_AXI_USER register. | Security | Required for Caliptra threat model |
| FUSE AXI USER Programming rules | It is strongly recommended that integrators set the FUSE valid AXI_USER to a non-zero value, due to the above uniqueness requirement. | Security | Required for Caliptra threat model |
| Manufacturing | SoC shall provision an IDevID certificate with fields that conform to the requirements described in Provisioning IDevID during manufacturing. | Statement of conformance | Functionality |
| Manufacturing | Caliptra relies on obfuscation for confidentiality of UDS_SEED. It is strongly advised to implement manufacturing policies to protect UDS_SEED as defense in depth measures. 1, Prevent leakage of UDS_SEED on manufacturing floor. 2. Implement policies to prevent cloning (programming same UDS_SEED into multiple devices). 3. Implement policies to prevent signing of spurious IDEVID certs. | Statement of conformance | Required for Caliptra threat model |
| Chain of trust | SoC shall ensure all mutable code and configuration measurements are stashed into Caliptra. A statement of conformance lists what is considered mutable code and configuration vs. what is not. The statement also describes the start of the boot sequence of the SoC and how Caliptra is incorporated into it. | Statement of conformance | Required for Caliptra threat model |
| Chain of trust | SoC shall limit the mutable code and configuration that persists across the Caliptra powergood reset. A statement of conformance lists what persists and why this persistence is necessary. | Statement of conformance | Required for Caliptra threat model |
| Implementation | SoC shall apply size-only constraints on cells tagged with the "u__size_only__" string and shall ensure that these are not optimized in synthesis and PNR | Statement of conformance | Required for Caliptra threat model |
| GLS FEV | GLS FEV must be run to make sure netlist and RTL match and none of the countermeasures are optimized away. See the following table for example warnings from synthesis runs to resolve through FEV | GLS simulations pass | Functional requirement |
Table 20: Caliptra synthesis warnings for FEV evaluation
| Module | Warning | Line No. | Description |
|---|---|---|---|
| sha512_acc_top | Empty netlist for always_comb | 417 | Unused logic (no load) |
| ecc_scalar_blinding | Netlist for always_ff block does not contain flip flop | 301 | Output width is smaller than internal signals, synthesis optimizes away the extra internal flops with no loads |
| sha512_masked_core | "masked_carry" is read before being assigned. Synthesized result may not match simulation | 295, 312 | |
| ecc_montgomerymultiplier | Netlist for always_ff block does not contain flip flop | 274, 326 | Output width is smaller than internal signals, synthesis optimizes away the extra internal flops with no loads |
| Multiple modules | Signed to unsigned conversion occurs |
OCP LOCK Caliptra ROM & Firmware Requirements
The following rules outline additional steps that are divided between Caliptra ROM and Caliptra Runtime firmware to ensure secure operation protocol compliance to OCP L.O.C.K. As described in the Caliptra Hardware Specification, when operating in OCP LOCK mode Key Vault slot 16 (KV16) is designated for holding the MDK and Key Vault slot 23 (KV23) is designated for holding the MEK.
- DOE error status (KV write failure):
Introduce a new status bit; ROM must check it after any DOE flow to ensure the KV write succeeded. - HEK DOE flow:
Must be locked after execution (like other DOE flows). ROM must run the HEK DOE flow even in non-LOCK contexts to lock it. Reset only by hard reset. - Derivations & locking:
- Derive the MDK to KV16, then set the lock_wr control bit of KV16.
- If OCP LOCK is enabled, derive HEK to KV22, then set the lock_wr control bit of KV23.
- Perform this before setting
OCP_LOCK_IN_PROGRESSsince HEK derivation depends on the HEK seed and standard CDI derivatives (from standard KV slots).
- Perform this before setting
- Setting lock state:
If OCP LOCK is enabled, ROM MUST setOCP_LOCK_IN_PROGRESSbefore transitioning to the First Mutable Code (FMC) image. - KV23 usage constraints:
Never attempt to write anything to KV23 except MEK (produced by AES).- DOE will flag an error and stall if a write to KV23 is attempted while OCP LOCK is enabled. Note that this differs from other cryptographic engines, which use the SS_OCP_LOCK_CTRL.LOCK_IN_PROGRESS bit to enforce the respective ruleset. This is because HEK seed deobfuscation is performed by the ROM prior to setting LOCK_IN_PROGRESS.
- DMA programming with KV:
The programmed byte count must match the value on the input strap_ss_key_release_key_size strap. The programmed destination address must match the value on the input strap_ss_key_release_base_addr. Violation of either of these rules will trigger a command decode error in the DMA. - KV filtering rules:
Cryptographic operations attempting to read from a Standard Key Vault slot and write a result to a LOCK Key Vault slot will fail (the same rule holds for the LOCK Key Vault slot β Standard Key Vault slot path). The error status is reported as a Key Vault Write failure in the respective cryptographic engine's register block. - AES API updates:
- Similar to all cryptographic core operations in Caliptra, firmware must set both the Key Vault Read Control and Key Vault Write Control registers prior to beginning the AES operation.
- When AES operation parameters match the configuration for which output data is written to KV23, result data is masked from being presented on the
dataoutregister. Also, the STATUS.OUTPUT_LOST register bit will be asserted to reflect that output data has been blocked. - AES operations used to decrypt Key Content into KV23 must be performed using automatic mode; manual operation is not supported for this process.
- Firmware shall execute the clear operation after any AES operation used to generate or load the MEK.
- Strap validation:
Read and confirm valid values for: - Firmware must clear any obfuscated MEK from memory immediately after use.
Integrator RTL modification requirements
Several files contain code that may be specific to an integrator's implementation and should be overridden. This overridable code is either configuration parameters with integrator-specific values or modules that implement process-specific functionality. Code in these files should be modified or replaced by integrators using components from the cell library of their fabrication vendor. The following table describes recommended modifications for each file.
Table 21: Caliptra integrator custom RTL file list
| File | Description |
|---|---|
| config_defines.svh | Enable Caliptra internal TRNG (if applicable). Declare name of custom clock gate module by defining USER_ICG. Enable custom clock gate by defining TECH_SPECIFIC_ICG. |
| soc_ifc_pkg.sv | Define AXI USER default behavior and (if applicable) override values. See Integration Parameters. |
| caliptra_icg.sv | Replace with a technology-specific clock gater. Modifying this file is not necessary if integrators override the clock gate module that is used by setting TECH_SPECIFIC_ICG. |
| beh_lib.sv | Replace rvclkhdr/rvoclkhdr with a technology-specific clock gater. Modifying this file may not be necessary if integrators override the clock gate module that is used by setting TECH_SPECIFIC_EC_RV_ICG. |
| beh_lib.sv | Replace rvsyncss (and rvsyncss_fpga if the design will be implemented on an FPGA) with a technology-specific sync cell. |
| caliptra_prim_flop_2sync.sv | Replace with a technology-specific sync cell. |
| caliptra_2ff_sync.sv | Replace with a technology-specific sync cell. |
| dmi_jtag_to_core_sync.v | Replace with a technology-specific sync cell. This synchronizer implements edge detection logic using a delayed flip flop on the output domain to produce a pulse output. Integrators must take care to ensure logical equivalence when replacing this logic with custom cells. |
Synthesis Constraints
To ensure the integrity of security countermeasures, silicon integrators must apply size_only (or an equivalent) constraint to all cells whose instance names contain the u__size_only__ tag. This prevents synthesis and Place-and-Route (PNR) tools from optimizing away critical redundant logic.
Primitive Instantiation
Caliptra uses redundant logic to counteract Fault Injection (FI) and Side Channel Analysis (SCA). This logic is built using generic primitive modules, which act as placeholders. Integrators must replace these generic primitives with corresponding technology-specific cells from their standard cell library. This abstraction allows for applying the necessary constraints directly to the technology cells.
The following generic primitives require replacement with technology-specific cells. Ensure the instance names for these replacement cells include the u__size_only__ tag, as shown in the example below.
caliptra_prim_generic_and2caliptra_prim_generic_bufcaliptra_prim_generic_clock_mux2caliptra_prim_generic_xnor2caliptra_prim_generic_xor2caliptra_prim_generic_flopcaliptra_prim_generic_flop_en
To integrate technology-specific replacements, set the CALIPTRA_PRIM_MODULE_PREFIX and CALIPTRA_PRIM_ROOT environment variables. For details on how CALIPTRA_PRIM_MODULE_PREFIX is used to select between generic and technology-specific modules, see the implementation in caliptra_prim_module_name_macros.svh.
Example: Technology-Specific Buffer
The following example shows how to create a technology-specific wrapper for the caliptra_prim_generic_buf primitive.
// In this example:
// - `CALIPTRA_PRIM_MODULE_PREFIX` is assumed to be `caliptra_prim_tech_name`.
// - `TECH_DEPENDENT_BUF` is the name of the technology-specific buffer cell.
// - `PORT_NAME_IN` and `PORT_NAME_OUT` are its input and output ports.
module caliptra_prim_tech_name_buf #(
parameter int Width = 1
) (
input logic [Width-1:0] in_i,
output logic [Width-1:0] out_o
);
for (genvar k = 0; k < Width; k++) begin : gen_bufs
// The instance name "u__size_only__buf" contains the required tag.
// Synthesis tools must be configured to apply "size_only"
// constraints to any instance whose name includes "u__size_only__".
// This naming convention should be used for all replaced primitives.
TECH_DEPENDENT_BUF u__size_only__buf (
.<PORT_NAME_IN>(in_i[k]),
.<PORT_NAME_OUT>(out_o[k])
);
end
endmodule : caliptra_prim_tech_name_buf
CDC analysis and constraints
Clock Domain Crossing (CDC) analysis is performed on the Caliptra core IP. The following are the results and recommended constraints for Caliptra integrators using standard CDC analysis EDA tools.
In an unconstrained environment, several CDC violations are anticipated. CDC analysis requires the addition of constraints to identify valid synchronization mechanisms and/or static/pseudo-static signals.
Analysis of missing synchronizers
- All of the signals, whether single-bit or multi-bit, originate from the CaliptraClockDomain clock and their endpoint is the JTAG clock domain.
- The violations occur on the read path to the JTAG.
- We only need to synchronize the controlling signal for this interface.
- Inside the dmi_wrapper, the dmi_reg_en and dmi_reg_rd_en comes from dmi_jtag_to_core_sync, which is a 2FF synchronizer.
The following code snippets and schematic diagrams illustrate the CDC violations that end at the JTAG interface.
Figure: Schematic diagram and code snippet showing JTAG-originating CDC violations





CDC analysis conclusions
- Missing synchronizers appear to be the result of βinferredβ and/or only 2-FF instantiated synchronizers.
- dmi_jtag_to_core_sync.v contains inferred 2FF synchronizers on the control signals βdmi_reg_wr_enβ and βdmi_reg_rd_enβ.
- 2FF synchronizer inferences are considered non-compliant and should be replaced by an explicitly instantiated synchronization module, which is intended to be substituted on a per-integrator basis.
- cdc report scheme two_dff -severity violation
- Multi-bit signals are effectively pseudo-static and are qualified by synchronized control qualifiers.
- Pseudo-static: wr_data, wr_addr
- cdc signal reg_wr_data -module dmi_wrapper -stable
- cdc signal reg_wr_addr -module dmi_wrapper -stable
- Pseudo-static: wr_data, wr_addr
- The core clock frequency must be at least twice the TCK clock frequency for the JTAG data to pass correctly through the synchronizers.
CDC constraints
- cdc report scheme two_dff -severity violation
- cdc signal reg_wr_data -module dmi_wrapper -stable
- cdc signal reg_wr_addr -module dmi_wrapper -stable
- cdc signal rd_data -module dmi_wrapper -stable
RDC analysis and constraints
Reset Domain Crossing (RDC) analysis is performed on the Caliptra core IP. The following are the results and recommended constraints for Caliptra integrators using standard RDC analysis EDA tools.
In an unconstrained environment, several RDC violations are anticipated. RDC analysis requires the addition of constraints to identify valid synchronization mechanisms from one reset domain to another.
Constraining reset domains
Reset domains
The following table identifies the major reset domains in Caliptra core IP design.
Table 22: Reset definitions (functional resets are marked in bold)
| Reset name | Reset type | Reset polarity | Definition point | Reset generated by |
|---|---|---|---|---|
| CPTRA_PWRGD | Async | Active Low | cptra_pwrgood | Primary Input |
| CPTRA_RST | Async | Active Low | cptra_rst_b | Primary Input |
| CPTRA_UC_RST | Async | Active Low | caliptra_top.soc_ifc_top1.i_soc_ifc_boot_fsm.cptra_uc_rst_b | Generated by Boot FSM |
| CPTRA_NON_CORE_RST | Async | Active Low | caliptra_top.soc_ifc_top1.i_soc_ifc_boot_fsm.cptra_noncore_rst_b | Generated by Boot FSM |
| RISCV_VEER_CORE_RST | Async | Active Low | caliptra_top.rvtop.veer.core_rst_l | AND of BOOT_FSM_CPTRA_UC_RST and RISCV_VEER_DBG_CORE_RST Functionally same as CPTRA_UC_RST |
| RISCV_VEER_DBG_DM_RST | Async | Active Low | caliptra_top.rvtop.veer.dbg.dbg_dm_rst_l | AND of CPTRA_PWRGD and a bit controlled from JTAG TAP Functionally same as CPTRA_PWRGD |
| CPTRA_JTAG_RST | Async | Active Low | jtag_trst_n | Primary Input |
Reset structure
The reset definitions can be visually represented as shown in the following diagram.
Figure: Reset tree for Caliptra

RDC false paths
The following table shows the false paths between various reset groups.
Table 23: Reset domain crossing false paths
| Launch flop reset | Capture flop reset | Comment |
|---|---|---|
| CPTRA_PWRGD | all other groups | CPTRA_PWRGD is the deepest reset domain, so all RDC paths from CPTRA_PWRGD can be set as false paths |
| CPTRA_RST | CPTRA_UC_RST | Boot FSM is reset by CPTRA_RST |
| CPTRA_RST | CPTRA_NON_CORE_RST | Boot FSM is reset by CPTRA_RST |
| CPTRA_NON_CORE_RST | CPTRA_RST | CPTRA_NON_CORE_RST can be asserted only by asserting CPTRA_RST |
| CPTRA_NON_CORE_RST | CPTRA_UC_RST | CPTRA_NON_CORE_RST can be asserted only by asserting CPTRA_RST Asserting CPTRA_RST means CPTRA_UC_RST will be asserted |
Reset sequencing scenarios
The resets defined in Table 22 have the following sequencing phases, which are applicable for different reset scenarios: cold boot, cold reset, warm reset and firmware reset.
The reset sequencing is illustrated in the following waveform.
Figure: Reset sequencing waveform for Caliptra

Reset ordering
The following table defines the order in which resets can get asserted. A ">>" in a cell at row X and column Y indicates that if the reset in row X is asserted, the reset in row Y is also asserted. For rest of the cells (in which symbol ">>" is not present) the preceding assumption is not true and so the paths between those resets are potential RDC violations. The black cells are ignored because they are between the same resets.
Table 24: Reset sequence ordering constraints

Reset constraints and assumptions
The following set of constraints and assumptions must be provided before running RDC structural analysis of Caliptra Core IP.
- Caliptra Core IP assumes the primary reset inputs are already synchronized to their respective clocks. The integrator must add external reset synchronizers to achieve the same.
- cptra_pwrgood and cptra_rst_b resets must be synchronized to cptra_clk
- Deassertion of jtag_trst_n reset must be synchronized to jtag_clk. The signal can be asserted asynchronously.
- The following debug register, which is driven from JTAG, is not toggled during functional flow.
- u_caliptra.rvtop.veer.dbg.dmcontrol_reg[0] = 0
- Set scan_mode to 0 for functional analysis.
- Stamp or create functional resets for cptra_noncore_rst_b and cptra_uc_rst_b at the definition points, as mentioned in Table 22.
- Create funtional reset grouping - This step must be customized as per the EDA tool, which is being used for RDC analysis. The goal of this customization is to achieve the following three sequencing requirements/constraints.
- Gate all clocks when cptra_noncore_rst_b is asserted. This ensures that the capture flop clock is gated while the source flop's reset is getting asserted, thereby preventing the capture flop from becoming metastable. The result is when cptra_noncore_rst_b is going to be asserted, the following signals are constrained to be at 1 at around that time.
- soc_ifc_top1.i_soc_ifc_boot_fsm.rdc_clk_dis
- soc_ifc_top1.i_soc_ifc_boot_fsm.arc_IDLE
- Gate Veer core clock when cptra_uc_rst_b is asserted. This ensures that the capture flop clock is gated while the source flop's reset is getting asserted, thereby preventing the capture flop from becoming metastable. The result is when cptra_uc_rst_b is going to be asserted, the following signal is constrained to be at 1 at around that time.
- soc_ifc_top1.i_soc_ifc_boot_fsm.fw_update_rst_window
- Quiesce the AHB bus before cptra_uc_rst_b is asserted. Since the firmware reset request is triggered by software, it can ensure that all AHB transcations are flushed out before initiating a reset request. This is done by generating a signal, fw_update_rst_window, which is asserted (driven to 1) around the firmware reset assertion edge. When fw_update_rst_window is asserted, the force_bus_idle signal of the AHB decoder is driven to 1, which ensures that all AHB requestes are driven low. So when cptra_uc_rst_b is going to get asserted, the following signals are constrained to be at 0 at around that time.
- doe.doe_inst.i_doe_reg.s_cpuif_req
- ecc_top1.ecc_reg1.s_cpuif_req
- hmac.hmac_inst.i_hmac_reg.s_cpuif_req
- key_vault1.kv_reg1.s_cpuif_req
- pcr_vault1.pv_reg1.s_cpuif_req
- data_vault1.dv_reg1.s_cpuif_req
- sha512.sha512_inst.i_sha512_reg.s_cpuif_req
- soc_ifc_top1.i_ahb_slv_sif_soc_ifc.dv
- sha256.sha256_inst.i_sha256_reg.s_cpuif_req
- csrng.u_reg.u_ahb_slv_sif.dv
- entropy_src.u_reg.u_ahb_slv_sif.dv
- aes_inst.ahb_slv_sif_inst.dv
- aes_inst.aes_clp_reg_inst.s_cpuif_req
- The AES Core State Machine is assumed to be in the IDLE state
- aes_inst.aes_inst.u_aes_core.u_aes_control.gen_fsm[0].gen_fsm_p.u_aes_control_fsm_i.u_aes_control_fsm.aes_ctrl_cs[5:0] = 6'b1001
- Gate all clocks when cptra_noncore_rst_b is asserted. This ensures that the capture flop clock is gated while the source flop's reset is getting asserted, thereby preventing the capture flop from becoming metastable. The result is when cptra_noncore_rst_b is going to be asserted, the following signals are constrained to be at 1 at around that time.
- Constrain the RDC false paths as per Table 23.
RDC violations and waivers
Considering the given constraints, three sets of crossings were identified as RDC violations. All of them can be waived as explained in Table 25. Note that the report may differ across EDA tools due to variations in structural analysis, which can be influenced by a range of settings.
Table 25: Reset domain crossing violations
| Sl no | Launch reset | Launch flop | Capture reset | Capture flop |
|---|---|---|---|---|
| 1 | cptra_uc_rst_b | rvtop.veer.dec.tlu.exthaltff.genblock.dff.dout[7] | cptra_noncore_rst_b | cg.user_soc_ifc_icg.clk_slcg_0_generated_icg.p_clkgate.vudpi0.q |
| 2 | cptra_uc_rst_b | rvtop.veer.dec.tlu.exthaltff.genblock.dff.dout[7] | cptra_noncore_rst_b | cg.user_icg.clk_slcg_0_generated_icg.p_clkgate.vudpi0.q |
| 3 | cptra_uc_rst_b | rvtop.veer.Gen_AXI_To_AHB.lsu_axi4_to_ahb* | cptra_noncore_rst_b | entropy_src.u_entropy_src_core.u_caliptra_prim_packer_fifo_bypass.data_q* |
| 4 | cptra_uc_rst_b | rvtop.veer.Gen_AXI_To_AHB.lsu_axi4_to_ahb* | cptra_noncore_rst_b | entropy_src.u_entropy_src_core.u_caliptra_prim_packer_fifo_precon.data_q* |
| 5 | cptra_rst_b | s_axi_active | cptra_pwrgood | cg.user_soc_ifc_icg.clk_slcg_0_generated_icg.p_clkgate.vudpi0.q |
For violations in Sl No 1 and 2, the schematic for the crossing is shown in the following figure.
Figure: Schematic for RDC violations #1 and #2

This violation can be waived because if the CPU is halted, there is no way to trigger a firmware update reset as it is initiated by the microcontroller itself. Thus, it can be ensured that on cptra_uc_rst_b, cpu_halt_status will be at the reset value already, which in turn ensures that there are no glitches on the output clock of CG.
For violations in Sl No 3 and 4, the schematic for the crossing is shown in the following figure.
Figure: Schematic for RDC violations #3 and #4

In the preceding schematic, an RDC crossing violation is identified at the data_q flop, which is located on the right-hand side of the figure. The analysis for prim_packer_fifo_bypass is elaborated here, with the same scenario applying to prim_packer_fifo_recon.
The fifo load and wr_data equations are as follows:
assign pfifo_bypass_push = !es_bypass_mode ? 1'b0 : fw_ov_mode_entropy_insert ? fw_ov_fifo_wr_pulse : pfifo_postht_not_empty; assign pfifo_bypass_wdata = fw_ov_mode_entropy_insert ? fw_ov_wr_data : pfifo_postht_rdata;The following scenarios can occur.
Table 26: Reset domain crossing scenarios for #3 and #4 crossing
| #Case | es_bypass_mode | fw_ov_mode_entropy_insert | pfifo_bypass_push | pfifo_bypass_wdata | Comment |
|---|---|---|---|---|---|
| 1 | 0 | don't care | 0 | don't care | No RDC violations, since the recirculation mux on data_q (pink on lower right side) prevents the flop from loading. |
| 2 | 1 | 0 | pfifo_postht_rdata | pfifo_postht_rdata | No RDC violations, since recirculation mux switches to the leg that is on the same reset domain (shown in purple on left side) |
| 3 | 0 | 1 | fw_ov_fifo_wr_pulse | fw_ov_wr_data | RDC violations are suppressed because fw_ov_fifo_wr_pulse = 0 (the red data lines on cptra_uc_rst_b are not able to reach data_q because load_data is 0) |
Referring to the preceding table, if it can be ensured that fw_ov_fifo_wr_pulse is 0 when cptra_uc_rst_b is asserted, RDC crossings (Case #3) can be avoided. This condition is described in the preceding Constraints section.
For violation in Sl No 5, SOC should ensure that there are no active transactions on the AXI interface while asserting cptra_rst_b.
Synthesis findings
Synthesis has been performed at CALIPTRA_WRAPPER level which encompasses the following :-
- caliptra_top
- Memories instantiated outside using tech specific macros
Design converges at 400MHz 0.72V using an industry standard, advanced technology node as of 2025 April.
Note: Any synthesis warnings of logic optimization must be reviewed and accounted for.
Netlist synthesis data
The following table illustrates representative netlist synthesis results using industry standard EDA synthesis tools and tool configurations.
These metrics are inclusive of VeeR core, Caliptra logic, imem/dmem RAM, ROM.
The area is expressed in units of square microns.
The target foundry technology node is an industry standard, advanced technology node as of 2025 April.
Table 27: Netlist synthesis data
| IP Name | Combinational Area | Sequential Area | Memory Area | Total Area | Instance Count |
|---|---|---|---|---|---|
| CALIPTRA_WRAPPER | 59934 | 430763 | 319147 | 491159 | 1559238 |
ROM area is not accounted for in the above table.
NOTE: RTL2Syn formality is not run as of now, we will update the results once we have run the flow.
Recommended LINT rules
A standardized set of lint rules is used to sign off on each release. The lint policy may be provided directly to integrators upon request to ensure lint is clean in the SoC.
Terminology
The following terminology is used in this document.
Table 28: Terminology
| Abbreviation | Description |
|---|---|
| AHB | AMBA Advanced High-Performance Bus |
| AXI | AMBA Advanced eXtensible Interface |
| AES | Advanced Encryption Standard |
| BMD | Boot Media Dependent |
| BMI | Boot Media Integrated |
| ECC | Elliptic Curve Cryptography |
| ECO | Engineering Change Order (used to implement logic changes to a hardware design post-synthesis) |
| QSPI | Quad Serial Peripheral Interface |
| RISC | Reduced Instruction Set Computer |
| SHA | Secure Hashing Algorithm |
| SPI | Serial Peripheral Interface |
| UART | Universal Asynchronous Receiver Transmitter |
| UC | Microcontroller, referring to Caliptra's internal RISC-V processor core |
a2c404a

Adam's Bridge Hardware Specification
Version 1.0
Scope
This document defines technical specifications for a Adam's Bridge Post-Quantum Cryptography (PQC ML-DSA and ML-KEM) subsystem used in the Open Compute Project (OCP). This document shall comprise the Adam's Bridge technical specification.
Overview
This document provides definitions and requirements for a Adam's Bridge Post-Quantum Cryptography (PQC ML-DSA and ML-KEM) subsystem. The document then relates these definitions to existing technologies, enabling device and platform vendors to better understand those technologies in trusted computing terms.
Introduction
The advent of quantum computers poses a serious challenge to the security of cloud infrastructures and services, as they can potentially break the existing public-key cryptosystems, such as RSA and elliptic curve cryptography (ECC). Even though the gap between todayβs quantum computers and the threats they pose to current public-key cryptography is large, the cloud landscape should act proactively and initiate the transition to the post-quantum era as early as possible. To comply with that, the U.S. government issued a National Security Memorandum in May 2022 that mandated federal agencies to migrate to PQC by 2035 [1].
The long-term security of cloud computing against quantum attacks depends on developing lattice-based cryptosystems, which are among the most promising PQC algorithms that are believed to be hard for both classical and quantum computers. The American National Institute of Standards and Technology (NIST) recognized this and selected CRYSTALS-KYBER (ML-KEM) [2] and CRYSTALS-Dilithium (ML-DSA) [3], two lattice-based algorithms, as standards for post-quantum key-establishment and digital signatures, respectively, in July 2022. These cryptosystems are constructed on the hardness of the module learning-with-errors problem (M-LWE) in module lattices.
To transition to PQC, we must develop hybrid cryptosystems to maintain industry or government regulations, while PQC updates will be applied thoroughly. Therefore, classical cryptosystems, e.g. ECC, cannot be eliminated even if PQC will significantly be developed.
Adamβs bridge was a mythological structure that existed to cross the formidable gulf that existed between two land masses. Asymmetric cryptography to post quantum is a similar formidable gap that exists in the world of cryptography and Adamβs bridge is the work undertaken to bridge the gap by building post quantum cryptographic accelerators.
This document shares the architectural characteristics of the proposed post-quantum Adams Bridge implementation. The proposed work divides the operations in the algorithms into multiple stages and executes them using pipelined processing architecture. An optimized cascading method is used within each stage and fine-tune each module individually to exploit multi-levels of parallelism to accelerate post-quantum Dilithium computation on hardware platforms to address performance and complexity challenges of PQC implementation. The proposed architecture uses various optimization techniques, including multi-levels of parallelism, designing reconfigurable cores, and implementing interleaved and pipelined architecture achieving significant speedup while maintaining high security and scalability. This work can facilitate the adoption and deployment of PQC in cloud computing and enhance the security and efficiency of cloud services and applications in the post-quantum era.
Documentation
The project contains comprehensive documentation of all submodules for ML-DSA and ML-KEM:
Memory requirement
The following table shows the required memory instances for Adam's Bridge:
| Instance | Depth | Data Width | Strobe Width |
|---|---|---|---|
| abr_sk_mem_bank0 | 596 | 32 | |
| abr_sk_mem_bank1 | 596 | 32 | |
| abr_w1_mem | 512 | 4 | |
| abr_mem_inst0_bank0 | 832 | 96 | |
| abr_mem_inst0_bank1 | 832 | 96 | |
| abr_mem_inst1 | 576 | 96 | |
| abr_mem_inst2 | 1472 | 96 | |
| abr_mem_inst3 | 64 | 384 | |
| abr_sig_z_mem | 224 | 160 | 8 |
| abr_pk_mem | 64 | 320 | 8 |
All memories are modeled as 1 read 1 write port RAMs with a flopped read data. See abr_1r1w_ram.sv and abr_1r1w_be_ram.sv for examples. Strobe width describes the number of bits enabled by each strobe. All strobed memories are byte enabled in the design.
References:
[1] The White House, "National Security Memorandum on Promoting United States Leadership in Quantum Computing While Mitigating Risks to Vulnerable Cryptographic Systems," 2022. [Online]. Available: White House.
[2] NIST, "FIPS 203 Module-Lattice-Based Key-Encapsulation Mechanism Standard," August 13, 2024.
[3] NIST, "FIPS 204 Module-Lattice-Based Digital Signature Standard," August 13, 2024.
a2c404a

Adam's Bridge ML-DSA Hardware Specification
Version 1.0
ML-DSA Overview
ML-DSA (Module-Lattice-Based Digital Signature Algorithm) is a quantum-resistant digital signature scheme defined in FIPS 204 [3].
High-Level Overview
Adamβs Bridge ML-DSA accelerator has all the necessary components to execute a pure hardware PQC operation. The main operations that involve more computational complexity, such as NTT, hashing, and sampling units, are explained as follows.

The security level of ML-DSA defined by NIST are as follows:
| Algorithm Name | Security Level |
|---|---|
| ML-DSA-44 | Level-2 |
| ML-DSA-65 | Level-3 |
| ML-DSA-87 | Level-5 |
CNSA 2.0 only allows the highest security level (Level-5) for PQC which is ML-DSA-87, and Adams Bridge only supports ML-DSA-87 parameter set.
API
TheΒ ML-DSA-87Β architectureΒ inputsΒ andΒ outputsΒ areΒ describedΒ inΒ theΒ followingΒ table.
| Name | Input/Output | Operation | Size (Byte) |
|---|---|---|---|
| name | Output | All | 8 |
| version | Output | All | 8 |
| ctrl | Input | All | 4 |
| status | Output | All | 4 |
| entropy (SCA) | Input | All | 64 |
| seed | Input | Keygen | 32 |
| sign_rnd | Input | Sign | 32 |
| message | Input | Sign/Verify | 64 |
| verification result | Output | Verify | 64 |
| External_Mu | Input | Sign/Verify | 64 |
| message strobe | Input | Sign/Verify | 1 |
| ctx size | Input | Sign/Verify | 1 |
| ctx | Input | Sign/Verify | 255 (+1) |
| pk | Input/Output | Keygen/Verify | 2592 |
| signature | Input/Output | Sign/Verify | 4627 (+1) |
| sk_out (software only) | Output | Keygen | 4896 |
| sk_in | Input | Signing | 4896 |
| Interrupt | Output | All | 520 |
| --------------------------- | --------------- | --------------- | ------------- |
| Total | 18440 |
name
βRead-only register consists of the name of component.Β
versionΒ
βRead-only register consists of the version of component.Β
CTRLΒ
βThe control register consists of the following flags:Β
| Bits | Identifier | Access | Reset | Decoded | Name |
|---|---|---|---|---|---|
| [31:7] | - | - | - | - | |
| [6] | STREAM_MSG | w | 0x0 | - | |
| [5] | EXTERNAL_MU | w | 0x0 | - | |
| [4] | PCR_SIGN | w | 0x0 | - | |
| [3] | ZEROIZE | w | 0x0 | - | |
| [2:0] | CTRL | w | 0x0 | - |
βCTRLΒ
CTRL command field contains two bits indicating:
- βCtrl = 0b000Β
βNo Operation.Β
- βCtrl = 0b001Β
βTrigs the core to start the initialization and perform keygen operation.Β
- βCtrl = 0b010Β
βTrigs the core to start the signing operation for a message block. Β
- βCtrl = 0b011Β
βTrigs the core to start verifying a signature for a message block. Β
- Ctrl = 0b100Β
βTrigs the core to start the keygen+signing operation for a message block. Β This mode decreases storage costs for the secret key (SK) by recalling keygen and using an on-the-fly SK during the signing process.
ZEROIZE
Zeroize all internal registers: Zeroize all internal registers after process to avoid SCA leakage.
Software write generates only a single-cycle pulse on the hardware interface and then will be erased.
PCR_SIGN
Run PCR Signing flow: Run MLDSA KeyGen+Signing flow to sign PCRs.
EXTERNAL_MU
Enable External_Mu Mode.
The External_mu variant of ML-DSA modifies the standard signing and verifying process by allowing the precomputed mu to be externally provided instead of being internally derived from the message and public key. In this variant, the signing procedure accepts mu as an explicit input, making it suitable for environments where mu is generated offline for efficiency. While the core signing and verifying algorithm remains unchanged, the message input register is ignored in this mode.
STREAM_MSG
Enable streaming message mode.
In this mode, the controller will wait until it requires the message data and will assert the MSG_STREAM_READY bit in the status register. Once MSG_STREAM_READY is observed, the user should first set MSG_STROBE to 0xF.
The user can then write the message, one dword at a time, by writing to dword 0 of the message register. If the last dword is partial, the user must set the MSG_STROBE register to appropriately indicate the valid bytes. If the message is dword-aligned, a value of 0x0 must be written to the MSG_STROBE register to indicate the last dword, followed by a dummy write to the message register.
The flow must be terminated by writing to the message register after setting the MSG_STROBE to a non 0xF value. No partial dwords are allowed before the last dword indication. MSG_STROBE only needs to be programmed before the stream of full dwords, and before the final dword. Valid values of MSG_STROBE include 4'b1111, 4'b0111, 4'b0011, 4'b0001, and 4'b0000.
statusΒ
βThe read-only status register consists of the following flags:Β
| Bits | Identifier | Access | Reset | Decoded | Name |
|---|---|---|---|---|---|
| [31:4] | - | - | - | - | |
| [3] | ERROR | r | 0x0 | - | |
| [2] | MSG_STREAM_READY | r | 0x0 | - | |
| [1] | VALID | r | 0x0 | - | |
| [0] | READY | r | 0x0 | - |
READYΒ
βIndicates if the core is ready to process the inputs.Β
βVALIDΒ
βIndicates if the process is computed and the output is valid.
MSG_STREAM_READY
βIndicates if the core is ready to process the message.
βVALIDΒ
βIndicates if the process could not complete due to an error. For ML-DSA this status bit indicates an error while decoding the secret key. In Caliptra it could also indicate that pcr signing mode was enabled with a command other than Keygen+Signing.
entropy
Entropy is required for SCA countermeasures to randomize the inputs with no change in the outputs. The entropy can be any 512-bit value in [0 : 2^512-1].Β
TheΒ ML-DSA-87 countermeasure requires several random vectors to randomize the intermediate values. An internal mechanism is considered to take one random vector of 512-bit (i.e., entropy register) and generate the required random vectors for different countermeasures.
seed
Adams Bridge component seed register type definition 8 32-bit registers storing the 256-bit seed for keygen. The seed can be any 256-bit value in [0 : 2^256-1].
sign_rnd
This register is used to support both deterministic and hedge variants of ML-DSA. The content of this register is the only difference between the deterministic and hedged variant of ML-DSA.
- In the βhedgedβ variant, sign_rnd is the output of an RBG.
- In the βdeterministicβ variant, sign_rnd is a 256-bit string consisting entirely of zeroes.
message
When not in streaming message mode, this architecture supports PureML-DSA defined by NIST with an empty ctx. When streaming message mode is enabled, this field is ignored except for dword 0 which is used to stream in the message.
verification result
To mitigate a possible fault attack on Boolean flag verification result, a 64-byte register is considered. Firmware is responsible for comparing the computed result with a certain segment of signature (segment c~), and if they are equal the signature is valid.
A verification result of all 0s indicates a failed verification attempt. Firmware should reject any signature with an all 0 value for it's c segment.
msg strobe
A 4-bit indication of enabled bytes in the next dword of the streamed message. Users must first program this to 0xF after observing MSG_STREAM_READY, unless the message is less than 1 dword. If the final dword is partial, MSG_STROBE must be programmed appropriately before writing the final bytes. Dword aligned messages must program MSG_STROBE to 0x0 to indicate the message is done being streamed.
ctx size
A 8-bit indication of the size in bytes of the ctx to be used.
ctx
This register stores the ctx field. It is applied only during streaming message mode.
sk_out
This register stores the private key for keygen if seed is given by software. This register can be read by ML-DSA user, i.e., software, after keygen operation.
If seed comes from the key vault, this register will not contain the private key to avoid exposing secret assets to software.
sk_in
This register stores the private key for signing. This register should be set before signing operation.
pk
ML-DSA component public key register type definition storing the public key. This register can be read by Adams Bridge user after keygen operation, or be set before verifying operation.
signature
ML-DSA component signature register type definition storing the signature of the message. This register is read by Adams Bridge user after signing operation, or be set before verifying operation.
βPseudocodeΒ
βKeygenΒ
Input:
seed
entropy
Output:
sk_out
pk
// Wait for the core to be ready (STATUS flag should be 2'b01 or 2'b11)
read_data = 0
while read_data == 0:
read_data = read(ADDR_STATUS)
// Feed the required inputs
write(ADDR_SEED, seed)
write(ADDR_ENTROPY, entropy)
// Trigger the core for performing Keygen
write(ADDR_CTRL, KEYGEN_CMD) // (STATUS flag will be changed to 2'b00)
// Wait for the core to be ready and valid (STATUS flag should be 2'b11)
read_data = 0
while read_data == 0:
read_data = read(ADDR_STATUS)
// Reading the outputs
sk_out = read(ADDR_SK)
pk = read(ADDR_PK)
// Return the outputs
return sk_out, pk
βΒ
βSigningΒ
βInput:
msg
sk_in
sign_rnd
entropy
Output:
signature
// Wait for the core to be ready (STATUS flag should be 2'b01 or 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Feed the required inputs
write(ADDR_MSG, msg);
write(ADDR_SK_IN, sk_in);
write(ADDR_SIGN_RND, sign_rnd);
write(ADDR_ENTROPY, entropy);
// Trigger the core for performing Signing
write(ADDR_CTRL, SIGN_CMD); // (STATUS flag will be changed to 2'b00)
// Wait for the core to be ready and valid (STATUS flag should be 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Reading the outputs
signature = read(ADDR_SIGNATURE);
// Return the output (signature)
return signature;
βVerifyingΒ
Input:
msg
pk
signature
Output:
verification_result
// Wait for the core to be ready (STATUS flag should be 2'b01 or 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Feed the required inputs
write(ADDR_MSG, msg);
write(ADDR_PK, pk);
write(ADDR_SIGNATURE, signature);
// Trigger the core for performing Verifying
write(ADDR_CTRL, VERIFY_CMD); // (STATUS flag will be changed to 2'b00)
// Wait for the core to be ready and valid (STATUS flag should be 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Reading the outputs
verification_result = read(ADDR_VERIFICATION_RESULT);
// Return the output (verification_result)
return verification_result;
Keygen + SigningΒ
This mode decreases storage costs for the secret key (SK) by recalling keygen and using an on-the-fly SK during the signing process.
Input:
seed
msg
sign_rnd
entropy
Output:
signature
// Wait for the core to be ready (STATUS flag should be 2'b01 or 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Feed the required inputs
write(ADDR_SEED, seed);
write(ADDR_MSG, msg);
write(ADDR_SIGN_RND, sign_rnd);
write(ADDR_ENTROPY, entropy);
// Trigger the core for performing Keygen + Signing
write(ADDR_CTRL, KEYGEN_SIGN_CMD); // (STATUS flag will be changed to 2'b00)
// Wait for the core to be ready and valid (STATUS flag should be 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Reading the outputs
signature = read(ADDR_SIGNATURE);
// Return the output (signature)
return signature;
βΒ
Performance and Area Results
ML-DSA-87
The performance results for two operational frequencies, 400 MHz and 600 MHz, are presented in terms of latency (clock cycles [CCs]), time [ms], and performance [IOPS] as follows:
| Freq [MHz] | 400 | 600 | ||||
|---|---|---|---|---|---|---|
| "Unprotected" | Latency [CC] | Time [ms] | Performance [IOPS] | Time [ms] | Performance [IOPS] | |
| Keygen | 15,600 | 0.039 | 25,641 | 0.026 | 38,462 | |
| Signing (1 round) | 26,600 | 0.067 | 15,038 | 0.044 | 22,556 | |
| Signing (Ave) | 106,400 | 0.266 | 3,759 | 0.177 | 5,639 | |
| Verifying | 18,800 | 0.047 | 21,277 | 0.031 | 31,915 |
NOTE: Masking and shuffling countermeasures are integrated into the architecture and there is a work-in-progress to make it configureble to be enabled or disabled at synthesis time.
The area overhead associated with enabling these countermeasures is as follows:
| Freq [MHz] | 400 | 600 | ||||
|---|---|---|---|---|---|---|
| "Protected" | Latency [CC] | Time [ms] | Performance [IOPS] | Time [ms] | Performance [IOPS] | |
| keygen | 15,600 | 0.039 | 25,641 | 0.026 | 38,462 | |
| Signing (1 round) | 36,700 | 0.092 | 10,899 | 0.061 | 16,349 | |
| Signing (Ave) | 146,800 | 0.367 | 2,725 | 0.245 | 4,087 | |
| Verifying | 18,800 | 0.047 | 21,277 | 0.031 | 31,915 |
-
CNSA 2.0 only allows the highest security level (Level-5) for PQC which is ML-DSA-87, and Adams Bridge only supports ML-DSA-87 parameter set.
-
The requried area for the unprotected ML-DSA-87 is 0.0366mm2 @5nm:
- 0.0146mm2 for stdcell
- 0.0220mm2 for ram area for 57.38 KB memory.
-
The requried area for the protected ML-DSA-87 is 0.114mm2 @5nm:
- 0.0921mm2 for stdcell
- 0.0220mm2 for ram area for 57.38 KB memory.
-
The design is converging today at 600MHz at low, med & high voltage corners. (We have noticed the design converging to 1 GHz on a latest process node.)
Signing perofrmance
The signing operation is the most time-consuming part of the MLDSA algorithm. However, it is not constant-time due to the inherent nature of ML-DSA. The signing process involves a loop that continues until all validity checks are passed. The number of iterations depends on the provided privkey, message, and sign_rnd.
According to FIPS 204 recommendations, there is no mechanism to interrupt the signing loop. Nevertheless, for the ML-DSA-87 parameter set, the average number of required loops is 3.85.
Proposed architecture
The value of k and l is determined based on the security level of the system defined by NIST as follows:
| Algorithm Name | Security Level | k | l |
|---|---|---|---|
| ML-DSA-87 | Level-5 | 8 | 7 |
In the hardware design, using an instruction-set processor yields a smaller, simpler, and more controllable design. By fine-tuning hardware acceleration, we achieve efficiency without excessive logic overhead. We implement all computation blocks in hardware while maintaining flexibility for future extensions. This adaptability proves crucial in a rapidly evolving field like post-quantum cryptography (PQC), even amidst existing HW architectures.
The Customized Instruction-Set Cryptography Engine is designed to provide efficient cryptographic operations while allowing flexibility for changes in NIST ML-DSA standards and varying security levels. This proposal outlines the architecture, instruction set design, sequencer functionality, and hardware considerations for the proposed architecture. This architecture is typically implemented as an Intellectual Property (IP) core within an FPGA or ASIC, featuring a pipelined design for streamlined execution and interfaces for seamless communication with the host processor.
In our proposed architecture, we define specific instructions for various submodules, including SHAKE256, SHAKE128, NTT, INTT, etc. Each instruction is associated with an opcode and operands. By customizing these instructions, we can tailor the engine's behavior to different security levels.
To execute the required instructions, a high-level controller acts as a sequencer, orchestrating a precise sequence of operations. Within the architecture, several memory blocks are accessible to submodules. However, it's the sequencer's responsibility to provide the necessary memory addresses for each operation. Additionally, the sequencer handles instruction fetching, decoding, operand retrieval, and overall data flow management.
The high-level architecture of Adams Bridge controller is illustrated as follows:

Keccak architecture
Hashing operation takes a significant portion of PQC latency. All samplers need to be fed by hashing functions. i.e., SHAKE128 and SHAKE256. Therefore, to improve the efficiency of the implementation, one should increase efficiency on the Keccak core, providing higher throughput using fewer hardware resources. Keccak core requires 24 rounds of the sponge function computation. We develop a dedicated SIPO (serial-in, parallel-out) and PISO (parallel-in, serial-out) for interfacing with this core in its input and output, respectively.
We propose an approach to design hardware rejection sampling architecture, which can offer more efficiency. This approach enables us to cascade the Keccak unit to rejection sampler and polynomial multiplication units that results in avoiding the memory access.
In our optimized architecture, to reduce the failure probability due to the non-deterministic pattern of rejection sampling and avoid any stall cycle in polynomial multiplication, we use a FIFO to store sampled coefficients that match the speed of polynomial multiplication. The proposed sampler works in parallel with the Keccak core. Therefore, the latency for sampling unit is absorbed within the latency for a concurrently running Keccak core.
In the input side, there are two different situations:
- The given block from SIPO is the last absorbing round.
In this situation, the output PISO buffer should receive the Keccak state. - The given block from SIPO is not the last absorbing round.
In this situation, the output PISO buffer should not receive the Keccak state. However, the next input block from SIPO needs to be XORed with the Keccak state.
There are two possible scenarios when the Keccak state has to be saved in the PISO buffer on the output side:
- PISO buffer EMPTY flag is not set.
In this situation, Keccak should hold on and maintain the current state until EMPTY is activated and transfer the state into PISO buffer. - PISO buffer EMPTY flag is set.
In this situation, the state can be transferred to PISO buffer and the following round of Keccak (if any) can be started.
PISO Buffer
The output of the Keccak unit is used to feed four different samplers at varying data rates. The Parallel in Serial out buffer is a generic buffer that can take the full width of the Keccak state and deliver it to the sampler units at the appropriate data rates.
Input data from the Keccak can come at 1088 or 1344 bits per clock. During expand mask operation, the buffer needs to be written from a write pointer while valid data remains in the buffer. All other modes only require writing the full Keccak state into the buffer when it is empty.
Output data rate varies - 32 bits for RejBounded and SampleInBall, 80 bits for Expand Mask and 120 bits for SampleRejq.
Expand Mask architecture
Dilithium samples the polynomials that make up the vectors and matrices independently, using a fixed seed value and a nonce value that increases the security as the input for Keccak. Keccak is used to take these seed and nonce and generate random stream bits.
Expand Mask takes Ξ³-bits (20-bit for ML-DSA-87) and samples a vector such that all coefficients are in range of [-Ξ³+1, Ξ³]. It continues to sample all required coefficients, n=256, for a polynomial.
After sampling a polynomial with 256 coefficients, nonce will be changed and a new random stream will be generated using Keccak core and will be sampled by expand mask unit.
The output of this operation results in a l different polynomial while each polynomial includes 256 coefficients.
y1,0 y1,l-1
Expand Mask is used in signing operation of Dilithium. The output of expand mask sampler is stored into memory and will be used as an input for NTT module.
We propose an architecture to remove the cost of memory access since NTT needs input in a specific format, i.e., 4 coefficients per each memory address. To achieve this, we need to have a balanced throughput between all these modules to avoid large buffering or conflict between them.
High-level architecture is illustrated as follows:

Keccak interface to Expand Mask
Keccak is used in SHAKE-256 configuration for expand mask operation. Hence, it will take the input data and generate 1088-bit output after each round. We propose implementing of Keccak while each round takes 12 cycles. The format of input data is as follows:
Input data = Ο' | n
Where Ο' is seed with 512-bits, n=ΞΊ+r is the 16-bit nonce that will be incremented for each polynomial (r++) or if the signature is rejected by validity checks (++).
Since each bits (20-bit in for ML-DSA-87) is used for one coefficient, 256*20=5120 bits are required for one polynomial which needs 5 rounds (5120/1088=4.7) of Keccak.
To sample l polynomial (l=7 for ML-DSA-87), we need a total of 5*7 = 35 rounds of Keccak.
There are two paths for Keccak input. While the input can be set by controller for new nonce in the case of next polynomial or rejected signature, the loop path is used to rerun Keccak for completing the current sampling process with l polynomial.
Expand mask cannot take all 1088-bit output parallelly since it makes hardware architecture too costly and complex, and also there is no other input from Keccak for the next 12 cycles. Therefore, we propose a parallel-input serial-output (PISO) unit in between to store the Keccak output and feed rejection unit sequentially.
Expand Mask
This unit takes data from the output of SHAKE-256 stored in a PISO buffer. The required cycles for this unit is 4.7 rounds of Keccak for one polynomial and 35 rounds of Keccak for all required polynomial (l polynomial which l=7 for ML-DSA-87).
In our optimized architecture, this unit works in parallel with the Keccak core. Therefore, the latency for expand mask is absorbed within the latency for a concurrently running Keccak core.
Our proposed NTT unit takes four coefficients per cycle from one memory address. It helps to avoid memory access challenges and make the control logic too complicated. This implies that the optimal speed of the expand mask module is to sample four coefficients per cycle.
There are 4 rejection sampler circuits corresponding to each 20-bit input. The total coefficient after each round of Keccak is 1088/20 = 54.4 coefficients. We keep expand mask unit working in all cycles and generating 4 coefficients per cycle without any interrupt. That means 12*4=48 coefficients can be processed during each Keccak round.

After 12 cycles, 48 coefficients are processed by the expand mask unit, and there are still 128-bit inside PISO. To maximize the utilization factor of our hardware resources, Keccak core will check the PISO status. If PISO contains 4 coefficients or more (the required inputs for expand mask unit), EMPTY flag will not be set, and Keccak will wait until the next cycle. Hence, expand mask unit takes 13 cycles to process 52 coefficients, and the last 48-bit will be combined with the next Keccak round to be processed.

Performance of Expand Mask
Sampling a polynomial with 256 coefficients takes 256/4=64 cycles. The first round of Keccak needs 12 cycles, and the rest of Keccak operation will be parallel with expand mask operation.
For a complete expand mask for Dilithium ML-DSA-87 with 7 polynomials, 7*64+12=460 cycles are required using sequential operation. However, our design can be duplicated to enable parallel sampling for two different polynomials. Having two parallel design results in 268 cycles, while three parallel design results in 204 cycles at the cost of more resource utilization.
NTT architecture
The most computationally intensive operation in lattice-based PQC schemes is polynomial multiplication which can be accelerated using NTT. However, NTT is still a performance bottleneck in lattice-based cryptography. We propose improved NTT architecture with highly efficient modular reduction. This architecture supports NTT, INTT, and point-wise multiplication (PWM) to enhance the design from resource sharing point-of-view while reducing the pre-processing cost of NTT and post-processing cost of INTT.
Our NTT architecture exploits a merged-layer NTT technique using two pipelined stages with two parallel cores in each stage level, making 4 butterfly cores in total. Our proposed parallel pipelined butterfly cores enable us to perform Radix-4 NTT/INTT operation with 4 parallel coefficients. While memory bandwidth limits the efficiency of the butterfly operation, we use a specific memory pattern to store four coefficients per address.
An NTT operation can be regarded as an iterative operation by applying a sequence of butterfly operations on the input polynomial coefficients. A butterfly operation is an arithmetic operation that combines two coefficients to obtain two outputs. By repeating this process for different pairs of coefficients, the NTT operation can be computed in a logarithmic number of steps.
Cooley-Tukey (CT) and Gentleman-Sande (GS) butterfly configurations can be used to facilitate NTT/INTT computation. The bit-reverse function reverses the bits of the coefficient index. However, the bit-reverse permutation can be skipped by using CT butterfly for NTT and GS for INTT.

We propose a merged NTT architecture using dual radix-4 design by employing four pipelined stages with two parallel cores at each stage level.
The following presents the high-level architecture of our proposed NTT to take advantage of Merged architectural design:

The following figure illustrates a 16-point NTT data flow based on our proposed architecture:

A merged-layer NTT technique uses two pipelined stages with two parallel cores in each stage level, making 4 butterfly cores in total. The parallel pipelined butterfly cores enable us to perform Radix-4 NTT/INTT operation with 4 parallel coefficients.
However, NTT requires a specific memory pattern that may limit the efficiency of the butterfly operation. For Dilithium use case, there are n=256 coefficients per polynomial that requires log n=8 layers of NTT operations. Each butterfly unit takes two coefficients while difference between the indexes is 28-i in ith stage. That means for the first stage, the given indexes for each butterfly unit are (k, k+128):
Stage 1 input indexes: {(0, 128), {1, 129), (2, 130), β¦, (127, 255)}
Stage 2 input indexes: {(0, 64), {1, 65), (2, 66), β¦, (63, 127), (128, 192), (129, 193), β¦, (191, 255)}
β¦
Stage 8 input indexes: {(0, 1), {2, 3), (4, 5), β¦, (254, 255)}
There are several considerations for that:
- We need access to 4 coefficients per cycle to match the throughput into 2Γ2 butterfly units.
- An optimized architecture provides a memory with only one reading port, and one writing port.
- Based on the previous two notes, each memory address contains 4 coefficients.
- TheΒ initial coefficientsΒ are produced sequentially by Keccak and samplers. Specifically, they begin with 0 and continue incrementally up to 255. Hence, at the very beginning cycle, the memory contains (0, 1, 2, 3) in the first address, (4, 5, 6, 7) in second address, and so on.
- The cost of in-place memory relocation to align the memory content is not negligible. Particularly, it needs to be repeated for each stage.
While memory bandwidth limits the efficiency of the butterfly operation, we use a specific memory pattern to store four coefficients per address.Β Β
We propose a pipeline architecture the read memory in a particular pattern and using a set of buffers, the corresponding coefficients are fed into NTT block.
The initial memory contains the indexes as follows:
| Address | Initial Memory Content | |||
|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 3 |
| 1 | 4 | 5 | 6 | 7 |
| 2 | 8 | 9 | 10 | 11 |
| 3 | 12 | 13 | 14 | 15 |
| 4 | 16 | 17 | 18 | 19 |
| 5 | 20 | 21 | 22 | 23 |
| 6 | 24 | 25 | 26 | 27 |
| 7 | 28 | 29 | 30 | 31 |
| 8 | 32 | 33 | 34 | 35 |
| 9 | 36 | 37 | 38 | 39 |
| 10 | 40 | 41 | 42 | 43 |
| 11 | 44 | 45 | 46 | 47 |
| 12 | 48 | 49 | 50 | 51 |
| 13 | 52 | 53 | 54 | 55 |
| 14 | 56 | 57 | 58 | 59 |
| 15 | 60 | 61 | 62 | 63 |
| 16 | 64 | 65 | 66 | 67 |
| 17 | 68 | 69 | 70 | 71 |
| 18 | 72 | 73 | 74 | 75 |
| 19 | 76 | 77 | 78 | 79 |
| 20 | 80 | 81 | 82 | 83 |
| 21 | 84 | 85 | 86 | 87 |
| 22 | 88 | 89 | 90 | 91 |
| 23 | 92 | 93 | 94 | 95 |
| 24 | 96 | 97 | 98 | 99 |
| 25 | 100 | 101 | 102 | 103 |
| 26 | 104 | 105 | 106 | 107 |
| 27 | 108 | 109 | 110 | 111 |
| 28 | 112 | 113 | 114 | 115 |
| 29 | 116 | 117 | 118 | 119 |
| 30 | 120 | 121 | 122 | 123 |
| 31 | 124 | 125 | 126 | 127 |
| 32 | 128 | 129 | 130 | 131 |
| 33 | 132 | 133 | 134 | 135 |
| 34 | 136 | 137 | 138 | 139 |
| 35 | 140 | 141 | 142 | 143 |
| 36 | 144 | 145 | 146 | 147 |
| 37 | 148 | 149 | 150 | 151 |
| 38 | 152 | 153 | 154 | 155 |
| 39 | 156 | 157 | 158 | 159 |
| 40 | 160 | 161 | 162 | 163 |
| 41 | 164 | 165 | 166 | 167 |
| 42 | 168 | 169 | 170 | 171 |
| 43 | 172 | 173 | 174 | 175 |
| 44 | 176 | 177 | 178 | 179 |
| 45 | 180 | 181 | 182 | 183 |
| 46 | 184 | 185 | 186 | 187 |
| 47 | 188 | 189 | 190 | 191 |
| 48 | 192 | 193 | 194 | 195 |
| 49 | 196 | 197 | 198 | 199 |
| 50 | 200 | 201 | 202 | 203 |
| 51 | 204 | 205 | 206 | 207 |
| 52 | 208 | 209 | 210 | 211 |
| 53 | 212 | 213 | 214 | 215 |
| 54 | 216 | 217 | 218 | 219 |
| 55 | 220 | 221 | 222 | 223 |
| 56 | 224 | 225 | 226 | 227 |
| 57 | 228 | 229 | 230 | 231 |
| 58 | 232 | 233 | 234 | 235 |
| 59 | 236 | 237 | 238 | 239 |
| 60 | 240 | 241 | 242 | 243 |
| 61 | 244 | 245 | 246 | 247 |
| 62 | 248 | 249 | 250 | 251 |
| 63 | 252 | 253 | 254 | 255 |
Suppose memory is read in this pattern:
Addr: 0, 16, 32, 48, 1, 17, 33, 49, β¦, 15, 31, 47, 63
The input goes to the customized buffer architecture as follows:
| 0 | β | |||||||
|---|---|---|---|---|---|---|---|---|
| 1 | ||||||||
| 2 | ||||||||
| 3 |
Cycle 0 reading address 0
| 64 | β | 0 | ||||||
|---|---|---|---|---|---|---|---|---|
| 65 | 1 | |||||||
| 66 | 2 | |||||||
| 67 | 3 |
Cycle 1 reading address 16
| 128 | β | 64 | 0 | |||||
|---|---|---|---|---|---|---|---|---|
| 129 | 65 | 1 | ||||||
| 130 | 66 | 2 | ||||||
| 131 | 67 | 3 |
Cycle 2 reading address 32
| 192 | β | 128 | 64 | 0 | ||||
|---|---|---|---|---|---|---|---|---|
| 193 | 129 | 65 | 1 | |||||
| 194 | 130 | 66 | 2 | |||||
| 195 | 131 | 67 | 3 |
Cycle 3 reading address 48
| 4 | β | 192 | 128 | 64 | 0 | |||
|---|---|---|---|---|---|---|---|---|
| 5 | 193 | 129 | 65 | 1 | ||||
| 6 | 194 | 130 | 66 | 2 | ||||
| 7 | 195 | 131 | 67 | 3 |
Cycle 4 reading address 1
| 68 | β | 4 | 192 | 128 | 64 | |||
|---|---|---|---|---|---|---|---|---|
| 69 | 5 | 193 | 129 | 65 | 1 | |||
| 70 | 6 | 194 | 130 | 66 | 2 | |||
| 71 | 7 | 195 | 131 | 67 | 3 |
Cycle 5 reading address 17
The highlighted value in the first buffer contains the required coefficients for our butterfly units, i.e., (0, 128) and (64, 192). Since we merged the 1 and second layers of NTT stages, the output of the first parallel butterfly units need to exchange one coefficient and then the required input for the second parallel set of butterfly units is ready, i.e., (0, 64) and (128, 192).
After completing the first round of operation including NTT stage 1 and 2, the memory contains the following data:
| Address | Memory Content after 1&2 stages | |||
|---|---|---|---|---|
| 0 | 0 | 64 | 128 | 192 |
| 1 | 1 | 65 | 129 | 193 |
| 2 | 2 | 66 | 130 | 194 |
| 3 | 3 | 67 | 131 | 195 |
| 4 | 4 | 68 | 132 | 196 |
| 5 | 5 | 69 | 133 | 197 |
| 6 | 6 | 70 | 134 | 198 |
| 7 | 7 | 71 | 135 | 199 |
| 8 | 8 | 72 | 136 | 200 |
| 9 | 9 | 73 | 137 | 201 |
| 10 | 10 | 74 | 138 | 202 |
| 11 | 11 | 75 | 139 | 203 |
| 12 | 12 | 76 | 140 | 204 |
| 13 | 13 | 77 | 141 | 205 |
| 14 | 14 | 78 | 142 | 206 |
| 15 | 15 | 79 | 143 | 207 |
| 16 | 16 | 80 | 144 | 208 |
| 17 | 17 | 81 | 145 | 209 |
| 18 | 18 | 82 | 146 | 210 |
| 19 | 19 | 83 | 147 | 211 |
| 20 | 20 | 84 | 148 | 212 |
| 21 | 21 | 85 | 149 | 213 |
| 22 | 22 | 86 | 150 | 214 |
| 23 | 23 | 87 | 151 | 215 |
| 24 | 24 | 88 | 152 | 216 |
| 25 | 25 | 89 | 153 | 217 |
| 26 | 26 | 90 | 154 | 218 |
| 27 | 27 | 91 | 155 | 219 |
| 28 | 28 | 92 | 156 | 220 |
| 29 | 29 | 93 | 157 | 221 |
| 30 | 30 | 94 | 158 | 222 |
| 31 | 31 | 95 | 159 | 223 |
| 32 | 32 | 96 | 160 | 224 |
| 33 | 33 | 97 | 161 | 225 |
| 34 | 34 | 98 | 162 | 226 |
| 35 | 35 | 99 | 163 | 227 |
| 36 | 36 | 100 | 164 | 228 |
| 37 | 37 | 101 | 165 | 229 |
| 38 | 38 | 102 | 166 | 230 |
| 39 | 39 | 103 | 167 | 231 |
| 40 | 40 | 104 | 168 | 232 |
| 41 | 41 | 105 | 169 | 233 |
| 42 | 42 | 106 | 170 | 234 |
| 43 | 43 | 107 | 171 | 235 |
| 44 | 44 | 108 | 172 | 236 |
| 45 | 45 | 109 | 173 | 237 |
| 46 | 46 | 110 | 174 | 238 |
| 47 | 47 | 111 | 175 | 239 |
| 48 | 48 | 112 | 176 | 240 |
| 49 | 49 | 113 | 177 | 241 |
| 50 | 50 | 114 | 178 | 242 |
| 51 | 51 | 115 | 179 | 243 |
| 52 | 52 | 116 | 180 | 244 |
| 53 | 53 | 117 | 181 | 245 |
| 54 | 54 | 118 | 182 | 246 |
| 55 | 55 | 119 | 183 | 247 |
| 56 | 56 | 120 | 184 | 248 |
| 57 | 57 | 121 | 185 | 249 |
| 58 | 58 | 122 | 186 | 250 |
| 59 | 59 | 123 | 187 | 251 |
| 60 | 60 | 124 | 188 | 252 |
| 61 | 61 | 125 | 189 | 253 |
| 62 | 62 | 126 | 190 | 254 |
| 63 | 63 | 127 | 191 | 255 |
The same process can be applied in the next round to perform NTT stage 3 and 4.
| 0 | β | |||||||
|---|---|---|---|---|---|---|---|---|
| 64 | ||||||||
| 128 | ||||||||
| 192 |
Cycle 0 reading address 0
| 16 | β | 0 | ||||||
|---|---|---|---|---|---|---|---|---|
| 80 | 64 | |||||||
| 144 | 128 | |||||||
| 208 | 192 |
Cycle 1 reading address 16
| 32 | β | 16 | 0 | |||||
|---|---|---|---|---|---|---|---|---|
| 96 | 80 | 64 | ||||||
| 160 | 144 | 128 | ||||||
| 224 | 208 | 192 |
Cycle 2 reading address 32
| 48 | β | 32 | 16 | 0 | ||||
|---|---|---|---|---|---|---|---|---|
| 112 | 96 | 80 | 64 | |||||
| 176 | 160 | 144 | 128 | |||||
| 240 | 224 | 208 | 192 |
Cycle 3 reading address 48
| 1 | β | 48 | 32 | 16 | 0 | |||
|---|---|---|---|---|---|---|---|---|
| 65 | 112 | 96 | 80 | 64 | ||||
| 129 | 176 | 160 | 144 | 128 | ||||
| 193 | 240 | 224 | 208 | 192 |
Cycle 4 reading address 1
| 17 | β | 1 | 48 | 32 | 16 | |||
|---|---|---|---|---|---|---|---|---|
| 81 | 65 | 112 | 96 | 80 | 64 | |||
| 145 | 129 | 176 | 160 | 144 | 128 | |||
| 209 | 193 | 240 | 224 | 208 | 192 |
Cycle 5 reading address 17
After completing all stages, the memory contents would be as follows:
| Address | Memory Content after Stage 7&8 | |||
|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 3 |
| 1 | 4 | 5 | 6 | 7 |
| 2 | 8 | 9 | 10 | 11 |
| 3 | 12 | 13 | 14 | 15 |
| 4 | 16 | 17 | 18 | 19 |
| 5 | 20 | 21 | 22 | 23 |
| 6 | 24 | 25 | 26 | 27 |
| 7 | 28 | 29 | 30 | 31 |
| 8 | 32 | 33 | 34 | 35 |
| 9 | 36 | 37 | 38 | 39 |
| 10 | 40 | 41 | 42 | 43 |
| 11 | 44 | 45 | 46 | 47 |
| 12 | 48 | 49 | 50 | 51 |
| 13 | 52 | 53 | 54 | 55 |
| 14 | 56 | 57 | 58 | 59 |
| 15 | 60 | 61 | 62 | 63 |
| 16 | 64 | 65 | 66 | 67 |
| 17 | 68 | 69 | 70 | 71 |
| 18 | 72 | 73 | 74 | 75 |
| 19 | 76 | 77 | 78 | 79 |
| 20 | 80 | 81 | 82 | 83 |
| 21 | 84 | 85 | 86 | 87 |
| 22 | 88 | 89 | 90 | 91 |
| 23 | 92 | 93 | 94 | 95 |
| 24 | 96 | 97 | 98 | 99 |
| 25 | 100 | 101 | 102 | 103 |
| 26 | 104 | 105 | 106 | 107 |
| 27 | 108 | 109 | 110 | 111 |
| 28 | 112 | 113 | 114 | 115 |
| 29 | 116 | 117 | 118 | 119 |
| 30 | 120 | 121 | 122 | 123 |
| 31 | 124 | 125 | 126 | 127 |
| 32 | 128 | 129 | 130 | 131 |
| 33 | 132 | 133 | 134 | 135 |
| 34 | 136 | 137 | 138 | 139 |
| 35 | 140 | 141 | 142 | 143 |
| 36 | 144 | 145 | 146 | 147 |
| 37 | 148 | 149 | 150 | 151 |
| 38 | 152 | 153 | 154 | 155 |
| 39 | 156 | 157 | 158 | 159 |
| 40 | 160 | 161 | 162 | 163 |
| 41 | 164 | 165 | 166 | 167 |
| 42 | 168 | 169 | 170 | 171 |
| 43 | 172 | 173 | 174 | 175 |
| 44 | 176 | 177 | 178 | 179 |
| 45 | 180 | 181 | 182 | 183 |
| 46 | 184 | 185 | 186 | 187 |
| 47 | 188 | 189 | 190 | 191 |
| 48 | 192 | 193 | 194 | 195 |
| 49 | 196 | 197 | 198 | 199 |
| 50 | 200 | 201 | 202 | 203 |
| 51 | 204 | 205 | 206 | 207 |
| 52 | 208 | 209 | 210 | 211 |
| 53 | 212 | 213 | 214 | 215 |
| 54 | 216 | 217 | 218 | 219 |
| 55 | 220 | 221 | 222 | 223 |
| 56 | 224 | 225 | 226 | 227 |
| 57 | 228 | 229 | 230 | 231 |
| 58 | 232 | 233 | 234 | 235 |
| 59 | 236 | 237 | 238 | 239 |
| 60 | 240 | 241 | 242 | 243 |
| 61 | 244 | 245 | 246 | 247 |
| 62 | 248 | 249 | 250 | 251 |
| 63 | 252 | 253 | 254 | 255 |
The proposed method saves the time needed for shuffling and reordering, while using only a little more memory.
With this memory access pattern, writes to the memory are in order (0, 1, 2, 3, etc) while reads wraparound with a step of 16 (0, 16, 32 48, 1, 17, 33, 49, etc). Hence, there will be a memory conflict where writes to addresses take place before the data is read and provided to the butterfly 2x2 module. To resolve this, a set of 3 base addresses are given to the NTT module β src address, interim address and dest address that belong to 3 separate sections in memory. The NTT module will access the memory with the appropriate base address for each round as follows:
| Round | Read base address | Write base address |
|---|---|---|
| 0 | src | interim |
| 1 | interim | dest |
| 2 | dest | interim |
| 3 | interim | dest |
At the end of NTT operation, results must be located in the section with the dest base address. This will also provide the benefit of preserving the original input for later use in keygen or signing operations. The same memory access pattern is followed for INTT operation as well. Note that Adamβs bridge controller may choose to make src and dest base addresses the same to save on memory usage, when original coefficients need not be preserved. In this case, the requirement is still to have a separate interim base address to avoid memory conflicts during NTT operation.
Modular Reduction in NTT
The modular addition/subtraction in hardware platform can be implemented by one additional subtraction/addition operations, as follows:

However, modular multiplication can be implemented using different techniques. The commonly used Barrett reduction and Montgomery reduction require additional multiplications and are more suitable for the non-specific modulus. Furthermore, Montgomery reduction needs two more steps to convert all inputs from normal domain to Montgomery domain and then convert back the results into normal domain. This conversation increases the latency of NTT operations and does not lead to the best performance. Hence, Barrett reduction and Montgomery reduction are expensive in time and hardware resources.

For Dilithium hardware accelerator, we can customize the reduction architecture based on the prime value of the scheme with q= 8,380,417 to design a hardware-friendly architecture that increase the efficiency of computation. The value of q can be presented by:
q=8,380,417=223-213+1
For the modular operation we have:
223=213-1 mod q
Suppose that all input operands are less than q, we have:
0β€a,b<q
z=a.b<q2=46'h3FE0_04FF_C001
Based on 223=213-1 mod q, we can rewrite the equation as follows:
z=223z45:23+z22:0=213z45:23-z45:23+z22:0=223z45:33+213z32:23-z45:23+z22:0=213z45:33-z45:33+213z32:23-z45:23+z22:0=223z45:43+213z42:33-z45:33+213z32:23-z45:23+z22:0=213z45:43-z45:43+213z42:33-z45:33+213z32:23-z45:23+z22:0=213z45:43+z42:33+z32:23+-z45:43-z45:33-z45:23+z22:0=213z45:43+z42:33+z32:23+z22:13+-z45:43-z45:33-z45:23+z12:0=213c-(z45:43+z45:33+z45:23)+z[12:0]
Where:
c=z45:43+z42:33+z32:23+z[22:13]<212
The value of c has 12 bits, and we can rewrite it as follows:
213c11:0=223c11:10+213c9:0=213c11:10-c11:10+213c9:0=213d-c11:10
d=c11:10+c9:0
So, the value of z mod q is as follows:
z=213c-z45:43+z45:33+z45:23+z12:0=213d+z12:0-z45:43+z45:33+z45:23+c11:10=213d+z12:0-e
Where:
e=z45:43+z45:33+z45:23+c11:10=f+z45:23 mod q
f[14:0]=z45:43+z45:33+c11:10
We use a modular addition for f+z45:23 to keep it less than q. This modular addition has one stage delay.
The addition between 213d and z12:0 can be implemented by concatenating since the first 13 bits of d are zero as follows:
g23:0=213d+z12:0=d10:0||z[12:0]
Since the result has more than 23 bits, we perform a modular addition to keep it less than q. For that, the regular modular addition will be replaced by the following architecture while c0=g23, r0=g[22:0]. In other words, c0=d10, r0[22:0]=d9:0||z[12:0]

The following figure shows the architecture of this reduction technique. As one can see, these functions do not need any multiplications in hardware and can be achieved by shifter and adder.

The modular multiplication is implemented with a 3-stage pipeline architecture. At the first pipeline stage, z=aΒ·b is calculated. At the second pipeline stage, f+z[45:23] and 213d+z12:0 are calculated in parallel. At the third pipeline stage, a modular subtraction is executed to obtain the result and the result is output.
We do not need extra multiplications for our modular reduction, unlike Barrett and Montgomery algorithms. The operations of our reduction do not depend on the input data and do not leak any information. Our reduction using the modulus q= 8,380,417 is fast, efficient and constant-time.
Performance of NTT
For a complete NTT operation with 8 layers, i.e., n = 256, the proposed architecture takes 82=4 rounds. Each round involves 2564=64 operations in pipelined architecture. Hence, the latency of each round is equal to 64 (read from memory) + 8 (2 sequential butterfly latency) + 4 (input buffer latency) + 2 (wait between each two stages) = 78 cycles.
Round 0: stage 0 & 1
Round 1: stage 2 & 3
Round 2: stage 4 & 5
Round 3: stage 6 & 7
The total latency would be 4Γ78=312 cycles.
For a complete NTT/INTT operation for Dilithium ML-DSA-87 with 7 or 8 polynomials, 7*312=2184 or 8*312=2496 cycles are required. However, our design can be duplicated to enable parallel NTT for two different polynomials. Having two parallel design results in 1248 cycles.
NTT shuffling countermeasure
To protect NTT, we have two options β shuffling the order of execution of coefficients and masking in-order computation such that NTT performs operation on two input shares per coefficient and produces two output shares. While masking is a strong countermeasure for side-channel attacks, it requires at least 4x the area and adds 4x the latency to one NTT operation. Shuffling is an implementation trick that can be used to provide randomization to some degree without area or latency overhead. In Adamβs Bridge, we employ a combination of both for protected design. Both NTT cores will have masking on the first layer of computation for INTT mode and will have a fully masked PWM module, in addition to shuffling. In NTT, PWA and PWS modes, the NTT cores can employ only shuffling.
| Address | Memory Content | ||||
|---|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 3 | |
| 1 | 4 | 5 | 6 | 7 | |
| 2 | 8 | 9 | 10 | 11 | |
| 3 | 12 | 13 | 14 | 15 | |
| 4 | 16 | 17 | 18 | 19 | |
| 5 | 20 | 21 | 22 | 23 | |
| 6 | 24 | 25 | 26 | 27 | |
| 7 | 28 | 29 | 30 | 31 | |
| 8 | 32 | 33 | 34 | 35 | |
| 9 | 36 | 37 | 38 | 39 | |
| 10 | 40 | 41 | 42 | 43 | |
| 11 | 44 | 45 | 46 | 47 | |
| 12 | 48 | 49 | 50 | 51 | |
| 13 | 52 | 53 | 54 | 55 | |
| 14 | 56 | 57 | 58 | 59 | |
| 15 | 60 | 61 | 62 | 63 | |
| 16 | 64 | 65 | 66 | 67 | |
| 17 | 68 | 69 | 70 | 71 | |
| 18 | 72 | 73 | 74 | 75 | |
| 19 | 76 | 77 | 78 | 79 | |
| 20 | 80 | 81 | 82 | 83 | |
| 21 | 84 | 85 | 86 | 87 | |
| 22 | 88 | 89 | 90 | 91 | |
| 23 | 92 | 93 | 94 | 95 | |
| 24 | 96 | 97 | 98 | 99 | |
| 25 | 100 | 101 | 102 | 103 | |
| 26 | 104 | 105 | 106 | 107 | |
| 27 | 108 | 109 | 110 | 111 | |
| 28 | 112 | 113 | 114 | 115 | |
| 29 | 116 | 117 | 118 | 119 | |
| 30 | 120 | 121 | 122 | 123 | |
| 31 | 124 | 125 | 126 | 127 | |
| 32 | 128 | 129 | 130 | 131 | |
| 33 | 132 | 133 | 134 | 135 | |
| 34 | 136 | 137 | 138 | 139 | |
| 35 | 140 | 141 | 142 | 143 | |
| 36 | 144 | 145 | 146 | 147 | |
| 37 | 148 | 149 | 150 | 151 | |
| 38 | 152 | 153 | 154 | 155 | |
| 39 | 156 | 157 | 158 | 159 | |
| 40 | 160 | 161 | 162 | 163 | |
| 41 | 164 | 165 | 166 | 167 | |
| 42 | 168 | 169 | 170 | 171 | |
| 43 | 172 | 173 | 174 | 175 | |
| 44 | 176 | 177 | 178 | 179 | |
| 45 | 180 | 181 | 182 | 183 | |
| 46 | 184 | 185 | 186 | 187 | |
| 47 | 188 | 189 | 190 | 191 | |
| 48 | 192 | 193 | 194 | 195 | |
| 49 | 196 | 197 | 198 | 199 | |
| 50 | 200 | 201 | 202 | 203 | |
| 51 | 204 | 205 | 206 | 207 | |
| 52 | 208 | 209 | 210 | 211 | |
| 53 | 212 | 213 | 214 | 215 | |
| 54 | 216 | 217 | 218 | 219 | |
| 55 | 220 | 221 | 222 | 223 | |
| 56 | 224 | 225 | 226 | 227 | |
| 57 | 228 | 229 | 230 | 231 | |
| 58 | 232 | 233 | 234 | 235 | |
| 59 | 236 | 237 | 238 | 239 | |
| 60 | 240 | 241 | 242 | 243 | |
| 61 | 244 | 245 | 246 | 247 | |
| 62 | 248 | 249 | 250 | 251 | |
| 63 | 252 | 253 | 254 | 255 |
To implement shuffling in NTT, the memory content is divided into 16 chunks. The highlighted section in the memory table above shows the 16 chunk start addresses. For example, the first chunk consists of addresses 0, 16, 32, 48. Second chunk has 1, 17, 33, 49, and so on. In NTT mode, memory read pattern is 0, 16, 32, 48, 1, 17, 33, 49, etc. The buffer in NTT module is updated to have two sections and is addressable to support INTT shuffling with matched search space as that of NTT mode.
During shuffling, two levels of randomization are done:
- Chunk order is randomized
- Start address within the selected chunk is also randomized.
With this technique, the search space is 16 Γ416=236. For example, if chunk 5 is selected as the starting chunk, the input buffer in NTT mode is configured as below.
| 2151 | 2140 | 2133 | 2122 |
| 1511 | 1500 | 1493 | 1482 |
| 871 | 860 | 853 | 842 |
| 231 | 220 | 213 | 202 |
Then, the order of execution is randomized for NTT as a second level of randomization. For example, order of execution in NTT mode can be (22, 86, 150, 214), (23, 87, 151, 215), (20, 84, 148, 212), (21, 85, 149, 213). Note that, once a random start address is selected, the addresses increment from that point and wrap around until all 4 sets of coefficients have been processed in order. Similarly, once a random chunk is selected, rest of the chunks are processed in order and wrapped around until all chunks are covered. In this example, chunk processing order is 5, 6, 7, 8, β¦, 15, 0, 1, 2, 3, 4.
For the next chunk, buffer pointer switches to the top half. While the bottom half of the buffer is read and executed, each location of the top half is filled in the same cycle avoiding latency overhead. When all 4 entries of the lower buffer are processed, upper buffer is ready to be fed into BF units.
| 2193 | 2183 | 2173 | 2163 |
|---|---|---|---|
| 1552 | 1542 | 1532 | 1522 |
| 911 | 901 | 891 | 881 |
| 270 | 260 | 250 | 240 |
| 2151 | 2140 | 2133 | 2122 |
| 1511 | 1500 | 1493 | 1482 |
| 871 | 860 | 853 | 842 |
| 231 | 220 | 213 | 202 |

Above figure shows the flow of a shuffled NTT/INTT operation. The shuffler part of the controller is responsible for calculating the correct memory addresses and feed the correct inputs to the BFs.
When shuffling in NTT mode, the memory read and write addresses are computed as shown below.for i in range (0, 4):
Read address \= chunk \+ (i \* RD\_STEP)
Write address = chunk * 4 + (rand_index * WR_STEP)
Where rand_index is the randomized start index of execution order for an NTT operation
E.g. if selected chunk is 5, and rand_index is 2
Order of execution is 2, 3, 0, 1
Read address \= 5 \+ (0\*16), 5 \+ (1\*16), 5+(2\*16), 5+(3\*16) \= 5, 21, 37, 53
Write address \= (5\*4) \+ (2\*1), (5\*4) \+ (3\*1), (5\*4) \+ (0\*1), (5\*4) \+ (1\*1) \= 22, 23, 20, 21
Figure below shows the additional control logic required to maintain the shuffling mechanism.

The index and chunk are obtained from a random number source. Chunk refers to starting chunk number and index refers to start address within the selected chunk. To account for BF latency, the index and chunk must be delayed appropriately (for our design, this latency is 10 cycles) for use in the controller shuffler logic.
The general address calculation for NTT is:
mem read addr=chunk+(countregular*STEPrd)
Since reading memory can be in order, a regular counter is used to read all 4 addresses of the selected chunk.
mem write addr=chunkf10*4+indexf10*STEPwr
The buffer address calculation for NTT is:
buffer write addr=countregular
buffer read addr=countindex
Where f10 refers to delayed values by 10 cycles. In this logic, chunk is updated every 4 cycles and buffer pointer is toggled (to top or bottom half) every 4 cycles.
The general address calculation for INTT is:
mem read addr=(chunk*4)+(index*STEPrd)
mem write addr=chunkf10+countregular*STEPwr
Since writing to memory can be in order, a regular counter is used to write all 4 addresses of the selected chunk.
In INTT, index need not be delayed since the BFs consume the coefficients in the next cycle.
The buffer address calculation for INTT is:
buffer write addr=indexf10
buffer read addr=countregular
Rejection Sampler architecture
Dilithium (or Kyber) samples the polynomials that make up the vectors and matrices independently, using a fixed seed value and a nonce value that increases the security as the input for Keccak. Keccak is used to take these seed and nonce and generate random stream bits.
Rejection sampler takes 24-bits (12 bits in case of Kyber) and checks if it is less than the prime number q = 223 β213+1 = 8380417 (q=3369 in case of Kyber). It continues to sample all required coefficients, n=256, for a polynomial.
After sampling a polynomial with 256 coefficients, nonce will be changed and a new random stream will be generated using Keccak core and will be sampled by rejection sampling unit.
The output of this operation results in a matrix of polynomial with k rows and l column while each polynomial includes 256 coefficients.
\[ \begin{bmatrix} A_{0,0} & \cdots & A_{0,l-1} \ \vdots & \ddots & \vdots \ A_{k-1,0} & \cdots & A_{k-1,l-1} \end{bmatrix} _{k*l} \] Rejection sampling is used in all three operations of Dilithium, i.e., keygen, sign, and verify. Since based on the specification of the Dilithium (and Kyber), the sampled coefficients are considered in NTT domain, the output of rejection sampler can directly be used for polynomial multiplication operation, as follows:
\[ \begin{bmatrix} A_{0,0} & \cdots & A_{0,l-1} \ \vdots & \ddots & \vdots \ A_{k-1,0} & \cdots & A_{k-1,l-1} \end{bmatrix} \circ \begin{bmatrix} s_{1,0} \ \vdots \ s_{1,l-1} \end
\begin{bmatrix} A_{0,0} \circ s_{1,0} + \cdots + A_{0,l-1} \circ s_{1,l-1} \ \vdots \ A_{k-1,0} \circ s_{1,0} + \cdots + A_{k-1,l-1} \circ s_{1,l-1} \end{bmatrix} \]
We propose an architecture to remove the cost of memory access from Keccak to rejection sampler, and from rejection sampler to polynomial multiplier. To achieve this, we need to have a balanced throughput between all these modules to avoid large buffering or conflict between them.
High-level architecture is illustrated as follows:

Keccak interface to Rejection Sampler
Keccak is used in SHAKE-128 configuration for rejection sampling operation. Hence, it will take the input data and generates 1344-bit output after each round. We propose implementing of Keccak while each round takes 12 cycles. The format of input data is as follows:
Input data = Ο | j | i
Where is seed with 256-bits, i and j are nonce that describes the row and column number of corresponding polynomial A such that:
Ai,j=Rejection_sampling(Keccak(Ο | j | i))
Since each 24-bit is used for one coefficient, each round of Keccak output provides 1344/24=56 coefficients. To have 256 coefficients for each polynomial (with same seed and nonce), we need to rerun Keccak for at least 5 rounds.
There are two paths for Keccak input. While the input can be set by controller for each new polynomial, the loop path is used to rerun Keccak for completing the previous polynomial.
Rejection sampler cannot take all 1344-bit output parallelly since it makes hardware architecture too costly and complex, and also there is no other input from Keccak for the next 12 cycles. Therefore, we propose a parallel-input serial-output (PISO) unit in between to store the Keccak output and feed rejection unit sequentially.
Rejection Sampler
This unit takes data from the output of SHAKE-128 stored in a PISO buffer. The required cycles for this unit are variable due to the non-deterministic pattern of rejection sampling. However, at least 5 Keccak rounds are required to provide 256 coefficients.
In our optimized architecture, this unit works in parallel with the Keccak core. Therefore, the latency for rejection sampling is absorbed within the latency for a concurrently running Keccak core.
Our proposed polynomial multiplier can perform point-wise multiplication on four coefficients per cycle that also helps to avoid memory access challenges and make the control logic too complicated. This implies that the optimal speed of the rejection sampling module is to sample four coefficients without rejection in one cycle.
On the output side, as the rejection sampling might fail, the rejection rate for each input is:
\[rejection_rate= 1-q/2^23=1-8380471/2^23=0.0009764β10^{-3}\]
Hence, the probability of failure to provide 4 appropriate coefficients from 4 inputs would be:
\[1-(1-rejection_rate)^4=0.00399\]
To reduce the failure probability and avoid any wait cycle in polynomial multiplication, 5 coefficients are fed into rejection while only 4 of them will be passed to polynomial multiplication. This decision reduces the probability of failure to
\[ 1 - (\text{probability of having 5 good inputs}) - (\text{probability of having 4 good inputs}) = 1 - (1 - \text{rejection_rate})^5 - \text{rejection_rate} \cdot \binom{5}{4} \cdot (1 - \text{rejection_rate})^4 = 0.00000998β10^{-5} \]
Adding a FIFO to rejection sampling unit can store the remaining unused coefficients and increase the probability of having 4 appropriate coefficients to match polynomial multiplication throughput. The architecture is as follows:

There are 5 rejection sampler circuits corresponding to each 24-bit input. The controller checks if each of these coefficients should be rejected or not. The valid input coefficients can be stored into the FIFO. While maximum 5 coefficients can be fed into FIFO, there are four more entries for the remaining coefficients from the previous cycle. There are several scenarios for the proposed balanced throughput architecture:
- At the very first cycle, or whenever the FIFO is empty, rejection sampling unit may not provide all 4 coefficients for polynomial multiplication unit. We reduce the failure probability of this scenario by feeding 5 coefficients, however, it may happen. So, for designing efficient architecture, instead of reducing the failure probability by adding more hardware cost, we use a VALID output that stops polynomial multiplier until all 4 required coefficients are sampled.
- If all 5 inputs are valid, they are going to be stored into FIFO. The first 4 coefficients will be sent to polynomial multiplication unit, while the remaining coefficients will be shifted to head of FIFO and be used for the next cycle with the first 3 valid coefficients from the next cycle.
- The maximum depth of FIFO is 9 entries. If all 9 FIFO entries are full, rejection sampling unit can provide the required output for the next cycles too. Hence, it does not accept a new input from Keccak core by raising FULL flag.
| Cycle count | Input from PISO | FIFO valid entries from previous cycle | Total valid samples | output | FIFO remaining for the next cycle |
|---|---|---|---|---|---|
| 0 | 5 | 0 | 5 | 4 | 1 |
| 1 | 5 | 1 | 6 | 4 | 2 |
| 2 | 5 | 2 | 7 | 4 | 3 |
| 3 | 5 | 3 | 8 | 4 | 4 |
| 4 | 5 | 4 | 9 | 4 | 5 (FULL) |
| 5 | 0 | 5 | 5 | 4 | 1 |
| 6 | 5 | 1 | 6 | 4 | 2 |
- If there is not FULL condition for reading from Keccak, all PISO data can be read in 12 cycles, including 11 cycles with 5 coefficients and 1 cycle for the 56th coefficient. This would be match with Keccak throughput that generates 56 coefficients per 12 cycles.
- The maximum number of FULL conditions is when there are no rejected coefficients for all 56 inputs. In this case, after 5 cycles with 5 coefficients, there is one FULL condition. After 12 cycles, 50 coefficients are processed by rejection sampling unit, and there are still 6 coefficients inside PISO. To maximize the utilization factor of our hardware resources, Keccak core will check the PISO status. If PISO contains 5 coefficients or more (the required inputs for rejection sampling unit), EMPTY flag will not be set, and Keccak will wait until the next cycle. Hence, rejection sampling unit takes 13 cycles to process 55 coefficients, and the last coefficients will be combined with the next Keccak round to be processed.

Performance of SampleRejq
For processing each round of Keccak using rejection sampling unit, we need 12 to 13 cycles that result in 60-65 cycles for each polynomial with 256 coefficients.
For a complete rejection sampling for Dilithium ML-DSA-87 with 8*7=56 polynomials, 3360 to 3640 cycles are required using sequential operation. However, our design can be duplicated to enable parallel sampling for two different polynomials. Having two parallel design results in 1680 to 1820 cycles, while three parallel design results in 1120 to 1214 cycles at the cost of more resource utilization.
INTT architecture
A merged-layer INTT technique uses two pipelined stages with two parallel cores in each stage level, making 4 butterfly cores in total. The parallel pipelined butterfly cores enable us to perform Radix-4 INTT operation with 4 parallel coefficients.
However, INTT requires a specific memory pattern that may limit the efficiency of the butterfly operation. For Dilithium use case, there are n=256 coefficients per polynomial that requires log n=8 layers of INTT operations. Each butterfly unit takes two coefficients while difference between the indexes is 2i-1 in ith stage. That means for the first stage, the given indexes for each butterfly unit are (2*k, 2*k+1):
Stage 1 input indexes: {(0, 1), {2, 3), (4, 5), β¦, (254, 255)}
Stage 2 input indexes: {(0, 2), {1, 3), (4, 6), β¦, (61, 63), (64, 66), (65, 67), β¦, (253, 255)}
β¦
Stage 8 input indexes: {(0, 128), {1, 129), (2, 130), β¦, (127, 255)}
There are several considerations for that:
- We need access to 4 coefficients per cycle to match the throughput into 2Γ2 butterfly units.
- An optimized architecture provides a memory with only one reading port, and one writing port.
- Based on the previous two notes, each memory address contains 4 coefficients.
- TheΒ initial coefficientsΒ are stored sequentially by NTT. Specifically, they begin with 0 and continue incrementally up to 255. Hence, at the very beginning cycle, the memory contains (0, 1, 2, 3) in the first address, (4, 5, 6, 7) in second address, and so on.
- The cost of in-place memory relocation to align the memory content is not negligible. Particularly, it needs to be repeated for each stage.
While memory bandwidth limits the efficiency of the butterfly operation, we use a specific memory pattern to store four coefficients per address.Β Β
We propose a pipeline architecture the read memory in a particular pattern and using a set of buffers, the corresponding coefficients are fed into INTT block.
The initial memory contains the indexes as follows:
| Address | Initial Memory Content | |||
|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 3 |
| 1 | 4 | 5 | 6 | 7 |
| 2 | 8 | 9 | 10 | 11 |
| 3 | 12 | 13 | 14 | 15 |
| 4 | 16 | 17 | 18 | 19 |
| 5 | 20 | 21 | 22 | 23 |
| 6 | 24 | 25 | 26 | 27 |
| 7 | 28 | 29 | 30 | 31 |
| 8 | 32 | 33 | 34 | 35 |
| 9 | 36 | 37 | 38 | 39 |
| 10 | 40 | 41 | 42 | 43 |
| 11 | 44 | 45 | 46 | 47 |
| 12 | 48 | 49 | 50 | 51 |
| 13 | 52 | 53 | 54 | 55 |
| 14 | 56 | 57 | 58 | 59 |
| 15 | 60 | 61 | 62 | 63 |
| 16 | 64 | 65 | 66 | 67 |
| 17 | 68 | 69 | 70 | 71 |
| 18 | 72 | 73 | 74 | 75 |
| 19 | 76 | 77 | 78 | 79 |
| 20 | 80 | 81 | 82 | 83 |
| 21 | 84 | 85 | 86 | 87 |
| 22 | 88 | 89 | 90 | 91 |
| 23 | 92 | 93 | 94 | 95 |
| 24 | 96 | 97 | 98 | 99 |
| 25 | 100 | 101 | 102 | 103 |
| 26 | 104 | 105 | 106 | 107 |
| 27 | 108 | 109 | 110 | 111 |
| 28 | 112 | 113 | 114 | 115 |
| 29 | 116 | 117 | 118 | 119 |
| 30 | 120 | 121 | 122 | 123 |
| 31 | 124 | 125 | 126 | 127 |
| 32 | 128 | 129 | 130 | 131 |
| 33 | 132 | 133 | 134 | 135 |
| 34 | 136 | 137 | 138 | 139 |
| 35 | 140 | 141 | 142 | 143 |
| 36 | 144 | 145 | 146 | 147 |
| 37 | 148 | 149 | 150 | 151 |
| 38 | 152 | 153 | 154 | 155 |
| 39 | 156 | 157 | 158 | 159 |
| 40 | 160 | 161 | 162 | 163 |
| 41 | 164 | 165 | 166 | 167 |
| 42 | 168 | 169 | 170 | 171 |
| 43 | 172 | 173 | 174 | 175 |
| 44 | 176 | 177 | 178 | 179 |
| 45 | 180 | 181 | 182 | 183 |
| 46 | 184 | 185 | 186 | 187 |
| 47 | 188 | 189 | 190 | 191 |
| 48 | 192 | 193 | 194 | 195 |
| 49 | 196 | 197 | 198 | 199 |
| 50 | 200 | 201 | 202 | 203 |
| 51 | 204 | 205 | 206 | 207 |
| 52 | 208 | 209 | 210 | 211 |
| 53 | 212 | 213 | 214 | 215 |
| 54 | 216 | 217 | 218 | 219 |
| 55 | 220 | 221 | 222 | 223 |
| 56 | 224 | 225 | 226 | 227 |
| 57 | 228 | 229 | 230 | 231 |
| 58 | 232 | 233 | 234 | 235 |
| 59 | 236 | 237 | 238 | 239 |
| 60 | 240 | 241 | 242 | 243 |
| 61 | 244 | 245 | 246 | 247 |
| 62 | 248 | 249 | 250 | 251 |
| 63 | 252 | 253 | 254 | 255 |
Suppose memory is read in the regular pattern:
Reading Addr: 0, 1, 2, 3, 4, β¦, 62, 63
The input goes to the butterfly architecture. The input values contain the required coefficients for our butterfly units in the next stage, i.e., (0, 1) and (2, 3). Since we merged the first and second layers of INTT stages, the output of the first parallel butterfly units need to exchange one coefficient and then the required input for the second parallel set of butterfly units is ready, i.e., (0, 2) and (1, 3).

To prepare the results for the next stages, the output needs to be stored into the customized buffer architecture as follows:
| 0 | β | |||||||
|---|---|---|---|---|---|---|---|---|
| 1 | ||||||||
| 2 | ||||||||
| 3 |
Cycle 0 after butterfly reading address 0
| 4 | β | 0 | ||||||
|---|---|---|---|---|---|---|---|---|
| 5 | 1 | |||||||
| 6 | 2 | |||||||
| 7 | 3 |
Cycle 0 after butterfly reading address 1
| 8 | β | 4 | 0 | |||||
|---|---|---|---|---|---|---|---|---|
| 9 | 5 | 1 | ||||||
| 10 | 6 | 2 | ||||||
| 11 | 7 | 3 |
Cycle 2 after butterfly reading address 2
| 12 | β | 8 | 4 | 0 | ||||
|---|---|---|---|---|---|---|---|---|
| 13 | 9 | 5 | 1 | |||||
| 14 | 10 | 6 | 2 | |||||
| 15 | 11 | 7 | 3 |
Cycle 3 after butterfly reading address 3
| 16 | β | 12 | 8 | 4 | 0 | |||
|---|---|---|---|---|---|---|---|---|
| 17 | 13 | 9 | 5 | 1 | ||||
| 18 | 14 | 10 | 6 | 2 | ||||
| 19 | 15 | 11 | 7 | 3 |
Cycle 4 after butterfly reading address 4
| 20 | β | 16 | 12 | 8 | 4 | |||
|---|---|---|---|---|---|---|---|---|
| 21 | 17 | 13 | 9 | 5 | 1 | |||
| 22 | 18 | 14 | 10 | 6 | 2 | |||
| 23 | 19 | 15 | 11 | 7 | 3 |
Cycle 5 after butterfly reading address 5
The highlighted value in the first buffer contains the required coefficients for our butterfly units in the next stage, i.e., (0, 4) and (8, 12).
However, we need to write the output in a particular pattern as follows:
Writing Addr: 0, 16, 32, 48, 1, 17, 33, 49, β¦, 15, 31, 47, 63
After completing the first round of operation including INTT stage 1 and 2, the memory contains the following data:
| Address | Memory Content after 1&2 stages | |||
|---|---|---|---|---|
| 0 | 0 | 4 | 8 | 12 |
| 1 | 16 | 20 | 24 | 28 |
| 2 | 32 | 36 | 40 | 44 |
| 3 | 48 | 52 | 56 | 60 |
| 4 | 64 | 68 | 72 | 76 |
| 5 | 80 | 84 | 88 | 92 |
| 6 | 96 | 100 | 104 | 108 |
| 7 | 112 | 116 | 120 | 124 |
| 8 | 128 | 132 | 136 | 140 |
| 9 | 144 | 148 | 152 | 156 |
| 10 | 160 | 164 | 168 | 172 |
| 11 | 176 | 180 | 184 | 188 |
| 12 | 192 | 196 | 200 | 204 |
| 13 | 208 | 212 | 216 | 220 |
| 14 | 224 | 228 | 232 | 236 |
| 15 | 240 | 244 | 248 | 252 |
| 16 | 1 | 5 | 9 | 13 |
| 17 | 17 | 21 | 25 | 29 |
| 18 | 33 | 37 | 41 | 45 |
| 19 | 49 | 53 | 57 | 61 |
| 20 | 65 | 69 | 73 | 77 |
| 21 | 81 | 85 | 89 | 93 |
| 22 | 97 | 101 | 105 | 109 |
| 23 | 113 | 117 | 121 | 125 |
| 24 | 129 | 133 | 137 | 141 |
| 25 | 145 | 149 | 153 | 157 |
| 26 | 161 | 165 | 169 | 173 |
| 27 | 177 | 181 | 185 | 189 |
| 28 | 193 | 197 | 201 | 205 |
| 29 | 209 | 213 | 217 | 221 |
| 30 | 225 | 229 | 233 | 237 |
| 31 | 241 | 245 | 249 | 253 |
| 32 | 2 | 6 | 10 | 14 |
| 33 | 18 | 22 | 26 | 30 |
| 34 | 34 | 38 | 42 | 46 |
| 35 | 50 | 54 | 58 | 62 |
| 36 | 66 | 70 | 74 | 78 |
| 37 | 82 | 86 | 90 | 94 |
| 38 | 98 | 102 | 106 | 110 |
| 39 | 114 | 118 | 122 | 126 |
| 40 | 130 | 134 | 138 | 142 |
| 41 | 146 | 150 | 154 | 158 |
| 42 | 162 | 166 | 170 | 174 |
| 43 | 178 | 182 | 186 | 190 |
| 44 | 194 | 198 | 202 | 206 |
| 45 | 210 | 214 | 218 | 222 |
| 46 | 226 | 230 | 234 | 238 |
| 47 | 242 | 246 | 250 | 254 |
| 48 | 3 | 7 | 11 | 15 |
| 49 | 19 | 23 | 27 | 31 |
| 50 | 35 | 39 | 43 | 47 |
| 51 | 51 | 55 | 59 | 63 |
| 52 | 67 | 71 | 75 | 79 |
| 53 | 83 | 87 | 91 | 95 |
| 54 | 99 | 103 | 107 | 111 |
| 55 | 115 | 119 | 123 | 127 |
| 56 | 131 | 135 | 139 | 143 |
| 57 | 147 | 151 | 155 | 159 |
| 58 | 163 | 167 | 171 | 175 |
| 59 | 179 | 183 | 187 | 191 |
| 60 | 195 | 199 | 203 | 207 |
| 61 | 211 | 215 | 219 | 223 |
| 62 | 227 | 231 | 235 | 239 |
| 63 | 243 | 247 | 251 | 255 |
The same process can be applied in the next round to perform INTT stage 3 and 4.
| 0 | β | |||||||
|---|---|---|---|---|---|---|---|---|
| 4 | ||||||||
| 8 | ||||||||
| 12 |
Cycle 0 after butterfly reading address 0
| 16 | β | 0 | ||||||
|---|---|---|---|---|---|---|---|---|
| 20 | 4 | |||||||
| 24 | 8 | |||||||
| 28 | 12 |
Cycle 1 after butterfly reading address 1
| 32 | β | 16 | 0 | |||||
|---|---|---|---|---|---|---|---|---|
| 36 | 20 | 4 | ||||||
| 40 | 24 | 8 | ||||||
| 44 | 28 | 12 |
Cycle 2 after butterfly reading address 2
| 48 | β | 32 | 16 | 0 | ||||
|---|---|---|---|---|---|---|---|---|
| 52 | 36 | 20 | 4 | |||||
| 56 | 40 | 24 | 8 | |||||
| 60 | 44 | 28 | 12 |
Cycle 3 after butterfly reading address 3
| 64 | β | 48 | 32 | 16 | 0 | |||
|---|---|---|---|---|---|---|---|---|
| 68 | 52 | 36 | 20 | 4 | ||||
| 72 | 56 | 40 | 24 | 8 | ||||
| 76 | 60 | 44 | 28 | 12 |
Cycle 4 after butterfly reading address 4
| 80 | β | 64 | 48 | 32 | 16 | |||
|---|---|---|---|---|---|---|---|---|
| 84 | 68 | 52 | 36 | 20 | 4 | |||
| 88 | 72 | 56 | 40 | 24 | 8 | |||
| 92 | 76 | 60 | 44 | 28 | 12 |
Cycle 5 after butterfly reading address 5
After completing all stages, the memory contents would be as follows:
| Address | Memory Content after Stage 7&8 | |||
|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 3 |
| 1 | 4 | 5 | 6 | 7 |
| 2 | 8 | 9 | 10 | 11 |
| 3 | 12 | 13 | 14 | 15 |
| 4 | 16 | 17 | 18 | 19 |
| 5 | 20 | 21 | 22 | 23 |
| 6 | 24 | 25 | 26 | 27 |
| 7 | 28 | 29 | 30 | 31 |
| 8 | 32 | 33 | 34 | 35 |
| 9 | 36 | 37 | 38 | 39 |
| 10 | 40 | 41 | 42 | 43 |
| 11 | 44 | 45 | 46 | 47 |
| 12 | 48 | 49 | 50 | 51 |
| 13 | 52 | 53 | 54 | 55 |
| 14 | 56 | 57 | 58 | 59 |
| 15 | 60 | 61 | 62 | 63 |
| 16 | 64 | 65 | 66 | 67 |
| 17 | 68 | 69 | 70 | 71 |
| 18 | 72 | 73 | 74 | 75 |
| 19 | 76 | 77 | 78 | 79 |
| 20 | 80 | 81 | 82 | 83 |
| 21 | 84 | 85 | 86 | 87 |
| 22 | 88 | 89 | 90 | 91 |
| 23 | 92 | 93 | 94 | 95 |
| 24 | 96 | 97 | 98 | 99 |
| 25 | 100 | 101 | 102 | 103 |
| 26 | 104 | 105 | 106 | 107 |
| 27 | 108 | 109 | 110 | 111 |
| 28 | 112 | 113 | 114 | 115 |
| 29 | 116 | 117 | 118 | 119 |
| 30 | 120 | 121 | 122 | 123 |
| 31 | 124 | 125 | 126 | 127 |
| 32 | 128 | 129 | 130 | 131 |
| 33 | 132 | 133 | 134 | 135 |
| 34 | 136 | 137 | 138 | 139 |
| 35 | 140 | 141 | 142 | 143 |
| 36 | 144 | 145 | 146 | 147 |
| 37 | 148 | 149 | 150 | 151 |
| 38 | 152 | 153 | 154 | 155 |
| 39 | 156 | 157 | 158 | 159 |
| 40 | 160 | 161 | 162 | 163 |
| 41 | 164 | 165 | 166 | 167 |
| 42 | 168 | 169 | 170 | 171 |
| 43 | 172 | 173 | 174 | 175 |
| 44 | 176 | 177 | 178 | 179 |
| 45 | 180 | 181 | 182 | 183 |
| 46 | 184 | 185 | 186 | 187 |
| 47 | 188 | 189 | 190 | 191 |
| 48 | 192 | 193 | 194 | 195 |
| 49 | 196 | 197 | 198 | 199 |
| 50 | 200 | 201 | 202 | 203 |
| 51 | 204 | 205 | 206 | 207 |
| 52 | 208 | 209 | 210 | 211 |
| 53 | 212 | 213 | 214 | 215 |
| 54 | 216 | 217 | 218 | 219 |
| 55 | 220 | 221 | 222 | 223 |
| 56 | 224 | 225 | 226 | 227 |
| 57 | 228 | 229 | 230 | 231 |
| 58 | 232 | 233 | 234 | 235 |
| 59 | 236 | 237 | 238 | 239 |
| 60 | 240 | 241 | 242 | 243 |
| 61 | 244 | 245 | 246 | 247 |
| 62 | 248 | 249 | 250 | 251 |
| 63 | 252 | 253 | 254 | 255 |
The proposed method saves the time needed for shuffling and reordering, while using only a little more memory.
INTT shuffling countermeasure
Similar to NTT operation, INTT requires shuffling the order of execution of coefficients in order to mitigate SCA attacks. Refer to section 5.3.3 for details on NTT shuffling. In INTT mode, the chunks are split in the following pattern into 16 chunks:
| Address | 1&2 | ||||
|---|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 3 | |
| 1 | 4 | 5 | 6 | 7 | |
| 2 | 8 | 9 | 10 | 11 | |
| 3 | 12 | 13 | 14 | 15 | |
| 4 | 16 | 17 | 18 | 19 | |
| 5 | 20 | 21 | 22 | 23 | |
| 6 | 24 | 25 | 26 | 27 | |
| 7 | 28 | 29 | 30 | 31 | |
| 8 | 32 | 33 | 34 | 35 | |
| 9 | 36 | 37 | 38 | 39 | |
| 10 | 40 | 41 | 42 | 43 | |
| 11 | 44 | 45 | 46 | 47 | |
| 12 | 48 | 49 | 50 | 51 | |
| 13 | 52 | 53 | 54 | 55 | |
| 14 | 56 | 57 | 58 | 59 | |
| 15 | 60 | 61 | 62 | 63 | |
| 16 | 64 | 65 | 66 | 67 | |
| 17 | 68 | 69 | 70 | 71 | |
| 18 | 72 | 73 | 74 | 75 | |
| 19 | 76 | 77 | 78 | 79 | |
| 20 | 80 | 81 | 82 | 83 | |
| 21 | 84 | 85 | 86 | 87 | |
| 22 | 88 | 89 | 90 | 91 | |
| 23 | 92 | 93 | 94 | 95 | |
| 24 | 96 | 97 | 98 | 99 | |
| 25 | 100 | 101 | 102 | 103 | |
| 26 | 104 | 105 | 106 | 107 | |
| 27 | 108 | 109 | 110 | 111 | |
| 28 | 112 | 113 | 114 | 115 | |
| 29 | 116 | 117 | 118 | 119 | |
| 30 | 120 | 121 | 122 | 123 | |
| 31 | 124 | 125 | 126 | 127 | |
| 32 | 128 | 129 | 130 | 131 | |
| 33 | 132 | 133 | 134 | 135 | |
| 34 | 136 | 137 | 138 | 139 | |
| 35 | 140 | 141 | 142 | 143 | |
| 36 | 144 | 145 | 146 | 147 | |
| 37 | 148 | 149 | 150 | 151 | |
| 38 | 152 | 153 | 154 | 155 | |
| 39 | 156 | 157 | 158 | 159 | |
| 40 | 160 | 161 | 162 | 163 | |
| 41 | 164 | 165 | 166 | 167 | |
| 42 | 168 | 169 | 170 | 171 | |
| 43 | 172 | 173 | 174 | 175 | |
| 44 | 176 | 177 | 178 | 179 | |
| 45 | 180 | 181 | 182 | 183 | |
| 46 | 184 | 185 | 186 | 187 | |
| 47 | 188 | 189 | 190 | 191 | |
| 48 | 192 | 193 | 194 | 195 | |
| 49 | 196 | 197 | 198 | 199 | |
| 50 | 200 | 201 | 202 | 203 | |
| 51 | 204 | 205 | 206 | 207 | |
| 52 | 208 | 209 | 210 | 211 | |
| 53 | 212 | 213 | 214 | 215 | |
| 54 | 216 | 217 | 218 | 219 | |
| 55 | 220 | 221 | 222 | 223 | |
| 56 | 224 | 225 | 226 | 227 | |
| 57 | 228 | 229 | 230 | 231 | |
| 58 | 232 | 233 | 234 | 235 | |
| 59 | 236 | 237 | 238 | 239 | |
| 60 | 240 | 241 | 242 | 243 | |
| 61 | 244 | 245 | 246 | 247 | |
| 62 | 248 | 249 | 250 | 251 | |
| 63 | 252 | 253 | 254 | 255 |
(0, 1, 2, 3) addresses are chunk0, (4,5,6,7) are chunk1, etc. Chunk addresses are in order since for INTT, reads are in order and writes are out of order. Similar to NTT, two levels of randomization are used:
- Starting chunk is randomized
- Start address within each chunk is randomized
With this technique, the search space is 16 Γ416=236. For example, if chunk 5 is selected as the starting chunk, and start address is 1, the order of execution is (21, 22, 23, 20). When next chunk is selected (chunk 6), start address is again randomized. For example, next order can be (27, 24, 25, 26) and so on.
The output buffer in INTT mode is configured as below. The buffer write pointer is aligned with the start address of the selected chunk and wraps around. In the given example, buffer write pointer increments as (1, 2, 3, 0) and the outputs of butterfly2x2 are stored in the buffer in locations (1, 2, 3, 0) in the order shown by superscript in the table below. This ensures the correct data is written back to memory to the correct addresses. In INTT mode, memory reads are randomized and memory writes will be in order.
| 1110 | 1100 | 1090 | 1080 |
|---|---|---|---|
| 1073 | 1063 | 1053 | 1043 |
| 1032 | 1022 | 1012 | 1002 |
| 991 | 981 | 971 | 961 |
| 952 | 942 | 932 | 922 |
| 911 | 901 | 891 | 881 |
| 870 | 860 | 850 | 840 |
| 833 | 823 | 813 | 803 |
While the next chunk starts, the data in the bottom half of the buffer is written to memory. Since every cycle there is a write to memory and write to the other half of the buffer, this incurs no latency overhead.
Memory read and write addresses are calculated as shown
for i in range (0, 4):
Read address \= (chunk\*4) \+ (rand\_index \* INTT\_RD\_STEP)
Write address = chunk + (i * INTT_WR_STEP)
Where rand_index is the randomized start index of execution order for an NTT operation
E.g. if selected chunk is 5, and rand_index is 2
Order of execution is 2, 3, 0, 1
Read address \= 5\*4 \+ (2\*1), 5\*4 \+ (3\*1), 5\*4+(0\*1), 5\*4+(1\*1) \= 22, 23, 20, 21
Write address \= 5 \+ (2\*16), 5 \+ (3\*16), 5 \+ (0\*16), 5 \+ (1\*16) \= 37, 53, 5, 21
Point-wise Multiplication architecture
Polynomial in NTT domain can be performed using point-wise multiplication (PWM). Considering the current architecture with 4 butterfly units, there are 4 modular multiplications that can be reused in point-wise multiplication operation. This approach enhances the design from an optimization perspective by resource sharing technique. `
There are 2 memories containing polynomial f and g, with 4 coefficients per each memory address. The parallel butterfly cores enable us to perform 4 point-wise multiplication operations with 4 parallel coefficients as follows:

The proposed NTT method preserves the memory contents in sequence without needing to shuffle and reorder them, so the point-wise multiplication can be sped up by using consistent reading/writing addresses from both memories.
PWM and INTT masking countermeasure
Masking countermeasure is implemented for two of the most critical operations in ML-DSA-87 to protect secrets - point-wise multiplication and INTT. A fully masked 2x2 butterfly architecture increases memory, area and latency by ~4 times. To avoid such large overhead, Adams Bridge implements a hybrid masking countermeasure where PWM operation is fully masked and the first stage of INTT operation is masked. To support Ay calculation where one input of PWM operation comes from samplers, an additional shares memory is provided that stores 1 polynomial worth of shares. Figure below shows the masking architecture:

PWM, PWM with accumulation and INTT modes of NTT engine support masking and shuffling countermeasures. Remaining opcodes (NTT, PWA and PWS) only support shuffling countermeasure. During Ay computation, the first PWM operation with masking enabled will receive inputs from the original coefficient memory/sampler and these inputs are in their original form. Internal to the NTT block, the inputs are split into shares and fed to the masked PWM units. The output shares are then stored in the share memory in split form. Subsequent PWM operations with accumulation and masking enabled will retrieve primary inputs from the original memory/sampler which are split on the fly and the accumulation input is retrieved from share memory. The accumulated output shares are stored back into the share memory.
The following masked INTT operation in Ay computation receives inputs from the share memory in split format. Once the first stage of INTT is finished, the shares are combined and passed onto the unmasked second stage of INTT. The final outputs of INTT are stored in the original coefficient memory in their combined form.
RejBounded architecture
This unit takes data from the output of SHAKE-256 stored in a PISO buffer. The required cycles for this unit are variable due to the non-deterministic pattern of sampling. However, at least 1 Keccak round is required to provide 256 coefficients.
This unit takes 4 bits from Keccak output. For ML-DSA-87 scheme, the only rejected sample is input data equal to 15 which means the probability of rejection is 116 .
| b (4-bit Input) | b mod 5 | 2-(b mod 5) | valid/invalid | Output mod q |
|---|---|---|---|---|
| 0 | 0 | 2 | valid | 2 |
| 1 | 1 | 1 | valid | 1 |
| 2 | 2 | 0 | valid | 0 |
| 3 | 3 | -1 | valid | 8380416 |
| 4 | 4 | -2 | valid | 8380415 |
| 5 | 0 | 2 | valid | 2 |
| 6 | 1 | 1 | valid | 1 |
| 7 | 2 | 0 | valid | 0 |
| 8 | 3 | -1 | valid | 8380416 |
| 9 | 4 | -2 | valid | 8380415 |
| 10 | 0 | 2 | valid | 2 |
| 11 | 1 | 1 | valid | 1 |
| 12 | 2 | 0 | valid | 0 |
| 13 | 3 | -1 | valid | 8380416 |
| 14 | 4 | -2 | valid | 8380415 |
| 15 | 0 | 2 | invalid | invalid |
In our optimized architecture, this unit works in parallel with the Keccak core. Therefore, the latency for RejBounded sampling is absorbed within the latency for a concurrently running Keccak core.
Our proposed NTT can perform on four coefficients per cycle that also helps to avoid memory access challenges and make the control logic too complicated. This implies that the optimal speed of the RejBounded sampling module is to sample four coefficients without rejection in one cycle.
On the output side, as the RejBouned sampling might fail, the rejection rate for each input is:
\[rejection_rate=1/16=0.0625\]
Hence, the probability of failure to provide 4 appropriate coefficients from 4 inputs would be:
\[1-(1-rejection_rate)^4=0.2275\]
To reduce the failure probability and avoid any wait cycle in polynomial multiplication, 5 coefficients are fed into rejection while only 4 of them will be passed to polynomial multiplication. This decision reduces the probability of failure to
\[ 1 - (\text{probability of having 5 good inputs}) - (\text{probability of having 4 good inputs}) = 1 - (1 - \text{rejection_rate})^5 - \text{rejection_rate} \cdot \binom{5}{4} \cdot (1 - \text{rejection_rate})^4 = 0.0344 \]
The following is the probability of failure for a design that has 4 samplings per cycle:
| Sample number in input | Failure probability of 4 valid output |
|---|---|
| 4 | 0.2275238037109375 |
| 5 | 0.034404754638671875 |
| 6 | 0.004229903221130371 |
| 7 | 0.0004580467939376831 |
| 8 | 4.549999721348286e-05 |
The following is the probability of failure for a high-throughput design that has 8 samplings per cycle:
| Sample number in input | Failure probability of 8 valid output |
|---|---|
| 8 | 0.4032805261667818 |
| 9 | 0.10492078925017267 |
| 10 | 0.021007113242376363 |
| 11 | 0.003525097407418798 |
| 12 | 0.0005203759357854665 |
| 13 | 6.966771504046676e-05 |
Adding a FIFO to RejBounded sampling unit can store the remaining unused coefficients and increase the probability of having 4 appropriate coefficients to match polynomial multiplication throughput. The architecture is as follows:

There are 8 rejection sampler circuits corresponding to each 4-bit input. The controller checks if each of these coefficients should be rejected or not. The valid input coefficients will be processes and a result between [-Ξ·, Ξ·] ( is 2 for ML-DSA-87) will be stored into the FIFO. While maximum 8 coefficients can be fed into FIFO, there are four more entries for the remaining coefficients from the previous cycle. There are several scenarios for the proposed balanced throughput architecture:
- At the very first cycle, or whenever the FIFO is empty, RejBounded sampling unit may not provide all 4 coefficients for polynomial multiplication unit. We reduce the failure probability of this scenario by feeding 8 coefficients, however, it may happen. So, for designing efficient architecture, instead of reducing the failure probability by adding more hardware cost, we use a VALID output that stops polynomial multiplier until all 4 required coefficients are sampled.
- If more than 4 inputs are valid, they are going to be stored into FIFO. The first 4 coefficients will be sent to polynomial multiplication unit, while the remaining coefficients will be shifted to head of FIFO and be used for the next cycle with the valid coefficients from the next cycle.
- The maximum depth of FIFO is 12 entries. The input needs 8 entities that are ready to use, and we know that 4 entities will be released in each cycle by sending the output. Hence, if more than 8 FIFO entries are full, RejBounded sampling unit does not accept a new input from Keccak core by raising FULL flag. However, it has the required valid samples to provide the required output for the next cycle.
| Cycle count | Input from PISO | FIFO valid entries from previous cycle | Total valid samples | output | FIFO remaining for the next cycle |
|---|---|---|---|---|---|
| 0 | 8 | 0 | 8 | 4 | 4 |
| 1 | 8 | 4 | 12 | 4 | 8 |
| 2 | 8 | 8 | 16 | 4 | 12 (FULL) |
| 3 | 0 | 12 | 12 | 4 | 8 |
| 4 | 8 | 8 | 16 | 4 | 12 (FULL) |
| 5 | 0 | 12 | 12 | 4 | 8 |
| 6 | 8 | 8 | 16 | 4 | 12 (FULL) |
| 7 | 0 | 12 | 12 | 4 | 8 |
| 8 | 7 | 8 | 15 | 4 | 11 (FULL) |
| 9 | 0 | 11 | 11 | 4 | 7 |
| 10 | 6 | 7 | 13 | 4 | 9 (FULL) |
- PISO contains 1088/4=272 coefficients. If there is not FULL condition for reading from Keccak, all PISO data can be read in 34 cycles. This would be match with Keccak throughput that generates 56 coefficients per 12 cycles. To maximize the utilization factor of our hardware resources, Keccak core will check the PISO status. If PISO contains 8 coefficients or more (the required inputs for RejBounded sampling unit), EMPTY flag will not be set, and Keccak will wait until the next cycle.
- The maximum number of FULL conditions is when there are no rejected coefficients for all inputs. In this case, after 2 cycles with 16 coefficients, there is one FULL condition. After 64 cycles, all 256 required coefficients are processed by RejBouned sampling unit.
- To maximize the utilization factor of our hardware resources, Keccak core will check the PISO status. If PISO contains 8 coefficients or more (the required inputs for RejBounded sampling unit), EMPTY flag will not be set, and Keccak will wait until the next cycle.

SampleInBall architecture
SampleInBall is a procedure that uses the SHAKE256 of a seed Ο to produce a random element of BΟ. The procedure uses the Fisher-Yates shuffle method. The signs of the nonzero entries of c are determined by the first 8 bytes of H(Ο), and the following bytes of H(Ο) are used to determine the locations of those nonzero entries.
We propose an architecture to remove the cost of memory access from Keccak to SampleInBall, and from SampleInBall to NTT. This requires a specific pattern of coefficients for NTT that prevents excessive buffering or interference between them. It also lowers the rejection rate and speeds up SampleInBall while maintaining small and efficient architecture.
High-level architecture is illustrated as follows:

Keccak interface to SampleInBall
Keccak is used in SHAKE-256 configuration for SampleInBall operation. Hence, it will take the input seed with 256-bit and generates 1088-bit output after each round.
The first Ο bits (Ο = 60 in the case of ML-DSA-87) in the first 8 bytes of this random stream are interpreted as Ο random sign bits si β {0, 1}, i = 0, . . . , Ο β 1. The remaining 64 β Ο bits are discarded.
The remaining random bits are used for sampling. Since each 8-bit is used for one sample, the first round of Keccak output provides (1088-64)/8=128 samples. The number of valid samples needed is 60. Because this is a sampling operation that is non-deterministic, if more samples are required, Keccak will run again and produce 1088/8=136 additional samples. Hence, there are two paths for Keccak input. While the input seed can be set by controller for each new polynomial c, the loop path is used to rerun Keccak for completing the previous polynomial.
SampleInBall cannot take all samples parallelly since it makes hardware architecture too costly and complex. Therefore, we propose a parallel-input serial-output (PISO) unit in between to store the Keccak output and feed SampleInBall unit sequentially.
SampleInBall
This unit takes data from the output of SHAKE-256 stored in a PISO buffer. The required cycles for this unit are variable due to the non-deterministic pattern of sampling. But this operation can only be finished with 60 valid samples.
In our optimized architecture, this unit works in parallel with the Keccak core. Therefore, the latency for Keccak sampling (for the second round and next) is absorbed within the latency for a concurrently running SampleInBall core.
The NTT unit needs to take four coefficients per cycle. This implies that the output contains four samples per each address as follows:

SampleInBall algorithm is as follows:
1) Initialize c = c0 c1 . . . c255 = 0 0 . . . 0
2) for i := 256 β Ο to 255
3) j β {0, 1, . . . , i}
4) s β {0, 1}
5) ci := cj
6) cj := (β1)s
7) return c
SampleInBall includes three main operations:
- Check the validity of input samples respect to parameter i (line 3 of algorithm)
- Store the sign s (line 4 of algorithm)
- Shuffle the stored polynomial c respect to parameter i, j, and s (line 5 and 6 of algorithm)
To recall, for ML-DSA-87 with Ο = 60, 60 valid samples are required.
Validity check on the input sample depends on the iteration number i while a sample greater than i will be rejected. Hence, the probability of failure for each round would be:

Rejection rate for each round of SampleInBall operation
To reduce the failure probability and avoid any wait cycle in polynomial multiplication, 4 samples are fed into SampleInBall while only 1 of them will be passed to shuffling unit. This decision reduces the probability of failure to:
\(probability of having 4 rejected inputs=(rejection rate)^4\)
In the worst case scenario (the first iteration with i=196), the failure probability is:
\((0.23046)^4=0.00282\)
The unused coefficients will be processed in the next cycle when i increments. The architecture is as follows:

Sampling phase of SampleInBall architecture
The first 2 cycles is used to store the sign bits into the sign buffer. After that, each 32 bits of input will be divided into 4 samples. Each sample is compared to i and the first valid sample is passed into the shuffling step.
Controller uses a counter to manage i value. The counter starts at 196 (for ML-DSA-87) and goes up after a valid coefficient is found.
When a valid coefficient is found, valid flag will be set and the chosen sample (known by j), i, and s will be transferred to shuffling unit. Then, the counter is incremented, and the remaining samples will be compared to the new i value.
The architecture of shuffling unit is as follows:

Shuffling phase of SampleInBall architecture
A polynomial c is stored in a memory that has four coefficients for each address. This pattern is needed for NTT operation that works on the output of SampleInBall. The memory has two ports that can read or write data.
Each input sample needs two cycles. In the first cycle, the memory reads the two addresses that have i and j, and in the second cycle, the memory saves the new coefficients.
The read data from address j will be updated with 1 or q-1 based on the s value, while the original value of j is transferred to address i using a multiplexer and demultiplexer.
When i and j have the same address, both ports try to write to the same location in the second cycle. To avoid this, the red path is used to turn off port a for address j. But then, j will be changed in the same buffer that has i (port b) and will be saved into memory.
Decompose Architecture
Decompose unit is used in signing operation of Dilithium. It decomposes r into (r1,r0) such that r β‘ r1(2Ξ³2)+r0 mod q. The output of decompose has two parts. While r0 will be stored into memory, r1 will be encoded and then be stored into Keccak SIPO input buffer to run SHAKE256.

There are k polynomials (k=8 for ML-DSA-87) that needs to be decomposed as follows:
\[ w= \begin{bmatrix} w_0 \ \vdots \ w_{k-1} \end{bmatrix} \]
Due to our memory configuration that stores 4 coefficients per address, we need 4 parallel cores for decompose and encode units to match the throughout between these modules.

DecomposeΒ algorithm plays a crucial role by breaking down the coefficients of a polynomial into smaller parts. Decompose calculates high and low bits r1 and r0 such that:
\[ r = r_1 Β· 2 Ξ³_2 + r_0 {mod} q \]
where:
\[ -Ξ³2 < r0 \leq Ξ³2 \]
except for the border case when r - r0=q β 1. In the border case, the high bits r1 are made zero, and the low bits r0 are reduced by one.
Definition:
- mod Ξ±: If Ξ± is a positive integer and m β Z or m β ZΞ±, then m mod Ξ± denotes the unique element m β²β Z in the range 0 β€ m β² < Ξ± such that m and m β² are congruent modulo Ξ±.
- modΒ± Ξ±: If Ξ± is a positive integer and m β Z or m β ZΞ±, then m modΒ± Ξ± denotes the unique element m β²β Z in the range βΞ±/2 < m β² β€ Ξ±/2 such that m and m β² are congruent modulo Ξ±.
High-level architecture is illustrated as follows:

The modular reduction architecture is as follows:

| r0 mod 22 down limit | r0 mod 22 Up limit | r0 |
|---|---|---|
| 0 | Ξ³2 | r0 mod 2 Ξ³2 |
| Ξ³2+1 | 2Ξ³2-1 | (r0 mod 2 Ξ³2) + (q-2 Ξ³2) |
Our suggested design calculates two shares of r0 and r1 at the same time. We use a lookup table with 16 parallel comparisons to find the value of r1 from r based on the following graph:

r1 value based on the given r
| r down limit | r Up limit | r1 |
|---|---|---|
| 0 | Ξ³2 | 0 |
| Ξ³2+1 | 3Ξ³2 | 1 |
| 3Ξ³2+1 | 5Ξ³2 | 2 |
| 5Ξ³2+1 | 7Ξ³2 | 3 |
| 7Ξ³2+1 | 9Ξ³2 | 4 |
| 9Ξ³2+1 | 11Ξ³2 | 5 |
| 11Ξ³2+1 | 13Ξ³2 | 6 |
| 13Ξ³2+1 | 15Ξ³2 | 7 |
| 15Ξ³2+1 | 17Ξ³2 | 8 |
| 17Ξ³2+1 | 19Ξ³2 | 9 |
| 19Ξ³2+1 | 21Ξ³2 | 10 |
| 21Ξ³2+1 | 23Ξ³2 | 11 |
| 23Ξ³2+1 | 25Ξ³2 | 12 |
| 25Ξ³2+1 | 27Ξ³2 | 13 |
| 27Ξ³2+1 | 29Ξ³2 | 14 |
| 29Ξ³2+1 | 31Ξ³2 | 15 |
| 31Ξ³2+1 | q-1 | 0 |
To compute r0 modΒ± 2Ξ³2, at the same time with r1 computation, a modular reduction operation will be applied to the input value r with respect to 2Ξ³2 to compute r0 mod 2Ξ³2. The result can be mapped into modΒ± 2Ξ³2 range by subtracting 2Ξ³2 when the result is greater than Ξ³2. However, at the end all shares should be modulus q. For that, instead of subtracting 2Ξ³2, we perform an addition with q-2Ξ³2 to the results.
The expected r0 for different values of r is illustrated as follows:

r0 value based on the given r (without considering boarder case)
Using this technique, we could achieve r0 and r1 for all cases expect the border case where r - r0=q β 1. This case can be detected inside r1 decompose architecture shown by red. In this case, the lookup table sets the value of 0 for r1, while r1 will be set as follows:
r0=r0-1=r-q+1-1=r-qβ‘r mod q

r0 value based on the given r considering boarder case
Performance of Decompose
There are k polynomials with 256 coefficients for each that need to be fed into decompose unit in pipeline method. After having 1088-bit input into SIPO, Keccak will be enabled parallel with decompose and encode units. However, the last round of Keccak will be performed after processing all coefficients. Each round of Keccak takes 12 cycles which allows processing of 48 coefficients. Since the output length of each encode unit is 4 bits, Keccak works faster than decompose/encode units and SIPO will not have overflow issue.
For a complete decompose/encode/hash operation for Dilithium ML-DSA-87 with 8 polynomials, 8*256/4 + 12 = 524 cycles are required using pipelined architecture.
MakeHint Architecture
The basic approach of the MakeHint(z, r) function involves decomposing both r and r+z into two parts: (r1, r0) for r and (rz1, rz0) for r+z. It then proceeds to evaluate whether r1 and rz1 are identical. In the event that r1 does not match rz1, it indicates that a hint is necessary to proceed. This process is essential for determining when additional information is required to resolve discrepancies between the compared segments.
However, decompose function is expensive on hardware to be implemented. Furthermore, performing a sequential decompose function using a shared hardware resource requires more latency.
The process of implementing the decompose function is notably resource-intensive and can incur significant costs when executed on hardware. Additionally, the sequential execution of this function, particularly when it relies on a common hardware resource, tends to introduce increased latency. This is due to the fact that shared resources often necessitate additional time to manage concurrent operations, which can result in delays and reduced efficiency.
The following architecture shows Dilithium algorithm to compute h=MakeHint(βct0, w β cs2 + ct0). There are several decompose operations embedded into HighBits, LowBits, and MakeHint functions, shown by red.

As an alternative and more efficient way, we can use a method to realize where hint needs to be generated as follows:
To compute h=MakeHint(βct0, w β cs2 + ct0), first note is that instead of computing (r1, r0) = Decomposeq (wβcs2, Ξ±) and checking whether βr0β<2 - Ξ² and r1 = w1, it is equivalent to just check that βw0-cs2β<2-Ξ², where w0 is the low part of w. If this check passes, w0 β cs2 is the low part of w β cs2. Next, recall that by the definition of the MakeHint function, a coefficient of a polynomial in h as computed is non-zero precisely if the high parts of the corresponding coefficients of w β cs2 and w β cs2 + ct0 differ. Now, we have already computed the full decomposition w = Ξ±w1 + w0 of w, and we know that Ξ±w1 + (w0 βcs2) is the correct decomposition of wβcs2. But then, Ξ±w1 + (w0 βcs2 +ct0) is the correct decomposition of w β cs2 + ct0 (i.e. the high part is w1) if and only if each coefficient of w0 β cs2 + ct0 lies in the interval (βΞ³2, Ξ³2], or, when some coefficient is βΞ³2 and the corresponding coefficient of w1 is zero. The last condition is due to the border case in the Decompose function. On the other hand, if these conditions are not true, then the high parts must differ, and it then follows that for computing the hint vector h it suffices to just check these conditions on the coefficients of w0 β cs2 + ct0. This algorithm is shown as follows:

The alternative algorithm reduces the decompose cost by modifying the MakeHint to enhance the efficiency of the architecture. We propose efficient architecture for performing the alternative MakeHint operation on hardware platform and accelerate this operation using a pipeline architecture.
High-level architecture is illustrated as follows:

Hint Sum and Hint BitPack
In Dilithium signing algorithm, the output of Makehint (hint output) is further processed to generate the encoded βhβ component of the signature. Additionally, one of the validity checks in signing algorithm uses hint sum to determine validity of the generated signature. These post-processing steps can be embedded into the Makehint architecture to avoid latency overhead while maintaining low complexity. The following figure shows the embedded logic into Makehint to generate the required outputs.

Hint Sum:
In the pipelined architecture, as 4 hints are generated every cycle, they are accumulated every cycle for all 8 polynomials.
{if hintsum> Ο invalid_h=1 else invalid_h=0
Hint BitPack:
The output of hint bitpack is a byte string βyβ of which [Ο-1:0] bytes are the indices at which the generated hint is non-zero. [Ο+k-1 : Ο] bytes are the total number of indices per polynomial at which the hint is non-zero. If the number of non-zero hints is < Ο, the rest of the entries of y are filled with 0s. If the number of non-zero hints is > Ο, Makehint flow continues for the remaining coefficients and the βyβ array is overwritten with the subsequent values. In this case, the βhβ component is invalid and the signature is discarded.
The following table shows an example of construction of y array per polynomial based on generated hints.
| Polynomial | Hint[3:0] | Index[3:0][7:0] | y[Ο-1:0] |
|---|---|---|---|
| 0 | 1-1-0-0 | 3-2-1-0 | 3-2 |
| 0 | 0-1-0-1 | 7-6-5-4 | 6-4-3-2 |
| 0 | 0-0-1-0 | 11-10-9-8 | 9-6-4-3-2 |
| β¦ | β¦ | β¦ | β¦-9-6-4-3-2 |
| 1 | 1-1-1-0 | 3-2-1-0 | 3-2-1-β¦-9-6-4-3-2 |
| 1 | 0-1-1-0 | 7-6-5-4 | 6-5-3-2-1-β¦-9-6-4-3-2 |
| β¦ | β¦ | β¦ | β¦-6-5-3-2-1-β¦-9-6-4-3-2 |
| 2 | 0-0-0-0 | 3-2-1-0 | β¦-6-5-3-2-1-β¦-9-6-4-3-2 |
| 2 | 0-0-0-1 | 7-6-5-4 | 4-β¦-6-5-3-2-1-β¦-9-6-4-3-2 |
| 2 | 1-1-0-0 | 11-10-9-8 | 11-10-4-β¦-6-5-3-2-1-β¦-9-6-4-3-2 |
| β¦ | β¦ | β¦ | β¦ |
To optimize area, 1 dword of βyβ buffer is written directly to the register API. The buffer generates a valid signal after accumulating 1 dword worth of data which can be used as a write enable for the register API. To protect the signature from intermittent firmware reads, the signature register is lockable. The lock is asserted during signing flow and is only unlocked after the entire flow has been completed.
At the end of all polynomials, the hintsum is written to the register API to construct the y[Ο+k-1:Ο] locations of the byte string.
It is possible that during the last cycle of the last polynomial, the index buffer contains < 1 dword of index values to be written to the reg API. To accommodate this scenario, the controller flushes out the buffer at the end of the last polynomial and writes the remaining data to the register API.
W1Encode Architecture
The signerβs commitment is shown by w, while this value needs to be decomposed into two shares to provide the required hint as a part of signature. The output of decompose is shown by (w1, w0) which presents the higher and lower parts of the given input. While w0 can be stored into the memory, the value of w1 is required to compute commitment hash using SHAKE256 operation. The following equation shows this operation:
c=H(ΞΌ||w1Encodew1)
Where is a 512-bit value computed based on the message and || means the concatenation between these two parts.
In ML-DSA-87 algorithm, there are 8 polynomials for w shown by w0 to w7. Furthermore, higher parts of each coefficient of these polynomials, shown by w1, is a value in [0:15] range that can be presented by 4 bits. Based on this, the total input size for performing SHAKE256 is:
inputsize=512size of ΞΌ+8poly number*256coeff per poly*4higher bit per coeff=8,704 bits
Since SHAKE256 takes only 1088 bits per each round, we have to feed these values sequentially. However, due to the prefix value of , and also the SHAKE256 input size does not divide evenly by each polynomial w1 size (which is 256*4=1024 bits), the pattern of feeding decompose results into hashing buffer is challenging.
There are k polynomials (k=8 for ML-DSA-87) that needs to be decomposed as follows:
\[ w= \begin{bmatrix} w_0 \ \vdots \ w_{k-1} \end{bmatrix} \]
Due to our memory configuration that stores 4 coefficients per address, we need 4 parallel cores for decompose and encode units to match the throughout between these modules.

To optimize the performance and remove the cost of memory, we use a pipeline architecture for encode that processes the input values and feed them into Keccak buffer. The following figure shows the optimized architecture for W1Encoder.

In the suggested design, every cycle, 4 coefficients for w1 are calculated from the decompose unit and then sent to the encode unit. Each coefficient is represented by 4 bits, making a total of 16 bits. However, the Keccak buffer only accepts input in 64-bit chunks. Therefore, a shift register is used to store the coefficients and pass a 64-bit chunk every 4 cycles to the Keccak SIPO buffer. An internal counter is used to control when the Keccak SIPO buffer takes this 64-bit chunk every 4 cycles.
Meanwhile, Keccak SIPO buffer can store 1088 bits of data and then activate Keccak to process it. The internal counter counts the number of writes on Keccak SIPO, and when it reaches 17 (17*64 = 1088 bits), Keccak enable is triggered. Because of Keccak architecture, SIPO can keep buffering a new input while Keccak works on the previous stored data.
The very first iteration of Keccak contains a 512-bit value shown by , which the high-level controller puts in Keccak SIPO buffer before decompose/encode process. So, only 576 bits of SIPO are left for encoding output. We suggest starting with a counter value of 8 to deal with this edge case in the first Keccak round. The other rounds have 17 SIPO write enable.
The following figure shows the counter architecture. The first two bits is used to enable SIPO buffer, while the remaining bits of [6:2] is used to enable Keccak process.

The following table reports the SIPO input for different Keccak rounds.
| Keccak SHAKE256 Round | Buffer input | Bits |
|---|---|---|
| 1 | ΞΌ | 512 |
| w0[143:0] | 576 | |
| 2 | w0[255:144] | 448 |
| w1[159:0] | 640 | |
| 3 | w1[255:160] | 384 |
| w2[175:0] | 704 | |
| 4 | w2[255:176] | 320 |
| w3[191:0] | 768 | |
| 5 | w3[255:192] | 256 |
| w4[207:0] | 832 | |
| 6 | w4[255:208] | 192 |
| w5[223:0] | 896 | |
| 7 | w5[255:224] | 128 |
| w6[239:0] | 960 | |
| 8 | w6[255:240] | 64 |
| w7[255:0] | 1024 | |
| 9 | padding | 1088 |
When the whole polynomials are done in the first 8 rounds of Keccak and Keccak done signal is asserted, the encode done signal is asserted and the high-level controller resumes control and adds the necessary padding to SIPO to finish the SHAKE256 process.
Norm Check Architecture
The figure provided illustrates the finite field range for polynomial coefficients. It indicates that each coefficient is an integer ranging from 0 to q-1:

The infinity norm is defined as follows:
For an element w β Z , \(β₯wβ₯β = |w|\), the absolute value of w. For an element \(w β Z_q\),\(β₯wβ₯β = w mod^ Β± q\) . For an element w of R or Rq, \(β₯wβ₯β = max0β€i<256 β₯wiβ₯β\) . For a length-m vector w with entries from R or Rq, \(β₯wβ₯β = max0β€i<m β₯w[i]β₯β\) .
In the context of norm definition within a finite field, when a value for the bound is provided, the norm check determines whether the coefficient falls below the bound or exceeds the q-bound, which is highlighted in red in the following figure.

There are three different validity checks with different bounds during signing operations as follows:
- \[ |(|z|)|_β β₯ Ξ³1 -Ξ² \]
- \[ |(|r0|)|_β β₯ Ξ³2 -Ξ² \]
- \[ |β¨β¨ct0β©β©|_β β₯ Ξ³2 \]
Vector z contains l polynomials (l=7 for ML-DSA-87) and r0 and ct0 contains k polynomials (k=8 for ML-DSA-87) that needs to be evaluated by norm check operation.
Due to our memory configuration that stores 4 coefficients per address, we need 4 parallel cores for norm check unit to match the throughput between these modules. To optimize the performance and remove the cost of memory, we use a pipeline architecture for norm check that processes the input values.

In the proposed design, during each cycle, 4 norm check coefficients are computed from stored data. This provides a performance improvement since this block only needs to read from memory and does not perform any writes to memory. Every coefficient is expressed using 24 bits, culminating in a combined total of 96 bits. A norm check is executed on each of these coefficients to produce a invalid output. The invalid outputs for all coefficients across all polynomials must be collectively ORed to yield the ultimate INVALID signal. This INVALID signal is asserted when any coefficient fails to meet the predetermined norm check criteria within the specified bound.
The proposed design is configurable and accepts different bounds to reduce the required hardware resources. The following table shows the latency requirements for these three norm checks.
| Input polynomials | Number of polynomials | Total coefficients | Latency for ML-DSA-87 |
|---|---|---|---|
| z | L | L*256 | 448 (for 4) |
| r0 | K | K*256 | 512 (for 4) |
| ct0 | k | K*256 | 512 (for 4) |
skDecode Architecture
The given sk to the core for performing a signing operation has been encoded, and skDecode is responsible to reverse the encoding procedure to divide sk to the appropriate portions. The initial segments within sk should be allocated to variables p, K, and tr, corresponding to sizes of 256 bits, 256 bits, and 512 bits, respectively, without necessitating any modifications.
The remaining part of sk stores the packed form of s1, s2, and t0, respectively. For s1 and s2, each coefficient is represented by Ξ· bits and needs to be unpacked as follows:
coefficient = Ξ· β data[Ξ·_size-1:0]
where Ξ·_size = bitlen(2*Ξ·).
For t0, each coefficient is represented by d bits and needs to be unpacked as follows:
coefficient = 2d-1 β data[d-1:0]
| sk part | poly size | coeff size | Total size | Latency for ML-DSA-87 |
|---|---|---|---|---|
| s1 | l=7 | Ξ·_size=3 | l*256*Ξ·_size=7*256*3= 5376 | 224 cycles |
| s2 | k=8 | Ξ·_size=3 | k*256*Ξ·_size =8*256*3= 6144 | 256 cycles |
| t0 | K=8 | d=13 | k*256*d=8*256*13= 26624 | 256 cycles (using 8 parallel cores) 512 cycles (using 4 parallel cores) |
The skDecode architecture reads 8 values from the register API, based on the memory pattern that has 4 coefficients for each address. It then maps them to modulo q value using the given equation. Then it stores the mapped value in the memory with two parallel write operations.
The high-level architecture for skDecode is as follows:

For s1 and s2 unpacking, the decode architecture is as follows:

In case a[2:0] is greater than βh4, the sk is considered out of range. skDecode block triggers an error interrupt to the RV core and the algorithm is aborted.
For t0 unpacking, the decode architecture is shown below:

Since key sizes are large, a key memory is used to interface with FW and skDecode module to avoid routing and timing issues. Assuming a memory interface of two 32-bit RW ports, s1s2 unpacking can be done with 8 parallel cores. This requires 24-bits per cycle to be processed which can be accommodated with a single key memory read per cycle (32-bits) and accumulating remaining bits in a sample buffer. Once next read occurs, bits are appended to the previous ones and the values are fed from buffer to the unpack module.
In case of t0 unpacking, since 13-bits are required per core, 4 parallel cores can be used instead of 8 to support the 32-bit memory interface. Two memory reads are done per cycle (64-bits) and 52 bits are processed per cycle (13*4). Remaining bits are accumulated in the sample buffer and read out.
The s1/s2 buffer can hold up to 32+24 bits of data. To avoid data conflict within s1/s2 buffer, the following memory access pattern is used:
S1, s2 unpack:
Cycle 0 π‘ͺ read 1 addr (buffer = 32 bits)
Cycle 1 π‘ͺ read 1 addr (buffer = 32 + 8 bits)
Cycle 2 π‘ͺ read 1 addr (buffer = 32 + 16 bits)
Cycle 3 π‘ͺ stall and read buffer contents (24 bits)
T0 unpack:
In case of t0 unpack, the t0 sample buffer can hold up to 64+52 = 116 bits of data. The buffer generates a full signal that is used to stall key memory reads for a cycle before continuing to write to the buffer.
sigEncode_z Architecture
The sigEncode_z operation converts a signature into a sequence of bytes. This operation has three distinct parts, namely c, z, and h. The first part simply writes the c into the register API as it is. The last part also uses the MakeHint structure to combine the MakeHint outputs into register API. However, the middle part, that is z, requires encoding.
Every coefficient of z is between [-Ξ³1+1, Ξ³1], but its value mod q is kept in memory. To encode it, this equation is needed on the non-modular value:
Encoded z = Ξ³1 β z
The high-level architecture processes each coefficient of z in this way:

The modular and non-modular value are equal when the z is in the interval [0, Ξ³1]. The first branch in this architecture shows this. But for the negative range, we need to remove q from the difference results. In this case, we get Ξ³1 β (z mod q) = Ξ³1 β (q + z) = Ξ³1 β z β q. So, the second branch adds q to the result to finish the encoding.
Using two parallel read ports, 8 encoding units read 8 coefficients from the memory and write the encoded values to the register API as follows:

Power2Round Architecture
Power2Round function is used to split each coefficient of vector t to two parts (similar to decompose unit). Power2Round calculates high and low bits r1 and r0 such that:
r = r1 Β· 2d +r0 mod q
where:
\(-2^(d-1)<r_0β€2^(d-1)\)
Definition:
- mod Ξ±: If Ξ± is a positive integer and m β Z or m β ZΞ±, then m mod Ξ± denotes the unique element m β²β Z in the range 0 β€ m β² < Ξ± such that m and m β² are congruent modulo Ξ±.
- modΒ± Ξ±: If Ξ± is a positive integer and m β Z or m β ZΞ±, then m modΒ± Ξ± denotes the unique element m β²β Z in the range βΞ±/2 < m β² β€ Ξ±/2 such that m and m β² are congruent modulo Ξ±.
The power2round process yields two outputs, t0 and t1. The value of t0 must be processed using skEncode and then placed in the API register. Meanwhile, t1 is to be processed with pkEncode and then fed into the register API. This is depicted in the high-level architecture diagram.

The goal is to create a pipeline architecture for skEncode and pkEncode that minimizes memory overhead costs. Therefore, these operations will be executed simultaneously with power2round, and the outcomes will be directly stored into the API.
Power2Round reads 2 addresses of memory containing 8 coefficients.
The expected r1 and r0 for different values of given r is illustrated as follows:


The diagram illustrates the structure of the power2round mechanism integrated with the skEncode arrangement. However, pkEncode is the process of saving the t1 values into the register API.

skEncode Architecture
The SkEncode operation requires multiple inputs (skEncode(Ο, K, tr, s1, s2, t0)). But Ο, K, tr, and t0 have been preloaded into the API through different operations. In terms of s1 and s2, SkEncode serves as a conversion tool that maps the values of these coefficients using the following equation:
data[Ξ·_size-1:0] = Ξ· β coefficient
For ML-DSA-87 with Ξ·=2, there are only 5 possible values for the s1 and s2 coefficients, and the following architecture converts their modular presentation into the packed format:

pkDecode Architecture
During the verification process, the provided pk must be decoded. The initial 256 bits of pk include Ο exactly as it is. The bits that follow consist of t1 polynomials. According to ML-DSA-87 protocol, each set of 10 bits represents a single coefficient. Therefore, these 10 bits need to be read, shifted left by 13 bits, and the outcome should be saved into memory.
The architecture is as follows:

sigDecode_z Architecture
The sigDecode operation reverses the sigEncode. This operation has three distinct parts, namely c, z, and h. The first part simply writes the c from the register API as it is. The last part also uses the HintBitUnpack structure to combine the MakeHint outputs into register API. However, the middle part, that is z, requires decoding.
Every coefficient of z is presented by 20 bits, but its value mod q is required to be stored into memory. To decode it, this equation is needed:
Decoded z = Ξ³1 β z
However, because the decoded value falls within [-Ξ³1+1, Ξ³1], we must convert it to its equivalent modular q value.
The high-level architecture is as follows:

sigDecode_h Architecture
The sigDecode function reverses sigEncode and is composed of three separate segments: c, z, and h. The h part uses the HintBitUnpack structure to decode given Hint and store it into memory.
We must reconstruct k polynomials (with k being 8 in the case of ML-DSA-87) using the decoding process provided. The total number of non-zero indices of each polynomial can be found in the range of bytes [Ο: Ο+k-1] (where Ο is set to 75 for ML-DAS-87). Therefore, the initial step to reconstruct each hi involves reading the byte at position Ο+i for each polynomial (where 0β€i<k), shown by HINTSUM_i.
HINTSUM_i indicates how many bytes to read from [Ο-1:0] byte range for the current polynomial from the register API. To keep the API constant and avoid any complexity in storing hints for the next polynomial, the decode module always reads 4 bytes from the register API and an internal vector, current polynomial map (curr_poly_map), is used to indicate which of the 4 bytes belongs to the current polynomial. For example, if the HINTSUM is 3, the curr_poly_map is updated to 0-1-1-1 to indicate that bytes [2:0] are valid. Byte[3] is processed as part of the next polynomial. Once the required bytes are identified, a 256-bit vector is updated with 1s in the positions indicated by these input bytes. After the first cycle, the first 4 bits of the bitmap are ready to be written to the memory.
To keep the throughput as storing 4 coefficients per cycle into internal memory, hints are processed every cycle and the bitmap is updated every cycle. To store data into memory, each coefficient, which might either be a 0 or a 1, is represented using 24 bits.
Since a non-zero hint can occur in any position of the 256-bit vector, it takes 64 cycles (4 coeffs/cycle) to write all 256 coefficients to memory irrespective of the value of the coefficient. For example, if the last 1 recorded is in index 35, the decode module continues to write the rest of the coefficients (36, 37, etc) to memory.

Example hint processing:
| Cycle | Polynomial | Hintsum | Hintsum_curr | Remaining hintsum | Byte select of reg API | Curr_poly_map |
|---|---|---|---|---|---|---|
| 0 | 0 | 5 | 5 | 5 | 3-2-1-0 | 1-1-1-1 |
| 1 | 0 | 5 | 5 | 5-4 = 1 | 7-6-5-4 | 0-0-0-1 |
| 2-63 | 0 | 5 | 5 | 0 | - | 0-0-0-0 |
| 64 | 1 | 11 | 11-5 = 6 | 6 | 8-7-6-5 | 1-1-1-1 |
| 65 | 1 | 11 | 6 | 6-4 = 2 | 12-11-10-9 | 0-0-1-1 |
| 66-127 | 1 | 11 | 6 | 0 | - | 0-0-0-0 |
| 128 | 2 | 25 | 25-11 = 14 | 14 | 14-13-12-11 | 1-1-1-1 |
| 129 | 2 | 25 | 14 | 14-4 = 10 | 18-17-16-15 | 1-1-1-1 |
| 130 | 2 | 25 | 14 | 10-4 = 6 | 22-21-20-19 | 1-1-1-1 |
| 131 | 2 | 25 | 14 | 6-4 = 2 | 26-25-24-23 | 0-0-1-1 |
In each cycle, the positions indicated by y[rd_ptr] are flipped to 1 in the bitmap. Once a polynomial is finished, the bitmap, read pointer, current polynomial map, etc are all reset to prepare for the next polynomial. In this way, sigdecode_h takes (64*8 = 512) cycles to finish writing all coefficients to the internal memory (a few additional cycles are required for control state transitions).
Hint rules
The hint (h segment of the signature) must follow a specific pattern. Any violation of these rules renders the hint (and signature) invalid. In such cases, the sigDecode_h architecture raises an error, causing the verification process to fail. The structure of h is as follows:
| Byte 0 | Byte 1 | Byte 2 | ... | Byte Ο-1 | Byte Ο | Byte Ο+1 | ... | Byte Ο+k-1 |
|---|---|---|---|---|---|---|---|---|
| Hint_0 | Hint_1 | Hint_2 | ... | Hint_Ο-1 | HINTSUM_0 | HINTSUM_1 | ... | HINTSUM_k-1 |
-
HINTSUM_0 represents the number of non-zero coefficients in poly_0.
-
For subsequent polynomials (poly_i, where i > 0), the number of non-zero coefficients is determined by HINTSUM_i - HINTSUM_(i-1).
The rules for a valid hint are as follows:
- The HINTSUM_i values must be in ascending order. Repeated values are allowed, meaning a polynomial may have no non-zero coefficients.
- The maximum allowable value for HINTSUM_i is Ο. Since the values must be in ascending order, if HINTSUM_i = Ο for any i < k-1, then all subsequent HINTSUM values must also be Ο.
- Within each polynomial, non-zero coefficient indices must be unique and arranged in ascending order.
- If HINTSUM_(k-1) is less than Ο, all hint values from Hint_(HINTSUM_(k-1)) to Hint_(Ο-1) must be zero.
UseHint Architecture
To reconstruct the signer's commitment, it is necessary to update the approximate computed value labeled as w' by utilizing the provided hint. Hence, the value of wβ should be decomposed, and its higher part should be altered if the related hint equals 1 for that coefficient. Subsequently, the higher part requires encoding through the W1Encode operation and must be stored into the Keccak SIPO.
The procedure is similar to the Decompose and W1Encode steps in the signing process, but it differs since there's no need to store the lower segment in memory. Moreover, the UseHint operation occurs between Decompose and W1Encode, adjusting the upper portion of the Decompose output utilizing h.
The Decompose and W1Encode stages of the signing procedure are depicted below (with the gray components being inactive):

The UseHint operation in the verifying operation is as follows (with the gray components being inactive):

For w0 condition we have:
- if w0=0 or w0 > Ξ³_2: w1βw1-1 mod 16
- else: w1βw1+1 mod 16
High-Level architecture
In our proposed architecture, we define specific instructions for various submodules, including SHAKE256, SHAKE128, NTT, INTT, etc. Each instruction is associated with an opcode and operands. By customizing these instructions, we can tailor the engine's behavior to different security levels.
To execute the required instructions, a high-level controller acts as a sequencer, orchestrating a precise sequence of operations. Within the architecture, several memory blocks are accessible to submodules. However, it's the sequencer's responsibility to provide the necessary memory addresses for each operation. Additionally, the sequencer handles instruction fetching, decoding, operand retrieval, and overall data flow management.
The high-level architecture of Adams Bridge controller is illustrated as follows:

Sequencer
High-Level controller works as a sequencer to perform a specific sequence of operations. There are several blocks of memory in the architecture that can be accessed by sub-modules. However, the sequencer would be responsible for passing the required addresses for each operation.
As an example, an NTT operation needs to take three base addresses as follows:
NTT(initialvalue_base_address, intermediatevalue_base_address, result_base_address)
So, for performing a=NTT(b), the sequencer needs to be:
NTT(a_base_address, temp_base_address, b_base_address)
The following table lists different operations used in the high-level controller.
Keccak and samplers Opcodes:
| Instruction | Description |
|---|---|
| RST_Keccak | Reset the Keccak SIPO buffer |
| EN_Keccak | Enable the Keccak |
| LDKeccak_MEM src, len | Load Keccak SIPO buffer at memory address src in len -width |
| LDKeccak_REG src, len | Load Keccak SIPO buffer at register ID src in len -width |
| RDKeccak_MEM dest, len | Read Keccak PISO buffer and store it at memory address dest in len -width |
| RDKeccak_REG dest, len | Read Keccak PISO buffer and store it at register ID dest in len -width |
| REJBOUND_SMPL dest | Start Keccak and RejBounded sampler and store the results at memory address dest |
| REJ_SMPL | Start Keccak and rejection sampler (results is used by PWM) |
| SMPL_INBALL | Start Keccak and SampleInBall (results is stored in SampleInBall memory) |
| EXP_MASK dest | Start Keccak and ExpandMask sampler and store the results at memory address dest |
NTT/INTT/PWO Opcodes:
| Instruction | Description |
|---|---|
| NTT src, temp, dest | Perform NTT on data at memory address src and store the results at address dest |
| INTT src, temp, dest | Perform INTT on data at memory address src and store the results at address dest |
| PWM src0, src1, dest | Perform PWM on data at memory address src0 and src1 and store the results at address dest (dest = src0*src1) |
| PWM_SMPL src, dest | Perform PWM on data from sampler and at memory address src and store the results at address dest (dest = smpl*src) |
| PWM_ACCU src0, src1, dest | Perform PWM in accumulation mode on data at memory address src0 and src1 and store the results at address dest (dest = src0*src1+dest) |
| PWM_ACCU_SMPL src, dest | Perform PWM in accumulation mode on data from sampler and at memory address src and store the results at address dest (dest = smpl*src + dest) |
| PWA src0, src1, dest | Perform PWA on data at memory address src0 and src1 and store the results at address dest (dest = src0+src1) |
| PWS src0, src1, dest | Perform PWS on data at memory src0 and src1 and store the results at address dest (dest = src0-src1) |
Other Opcodes:
| Instruction | Description |
|---|---|
| MAKEHINT src, dest | Perform MakeHint on data at memory address src and store the results at register API address dest |
| USEHINT src0, src1 | Perform Decompose on w data at memory address src0 considering the hint data at memory address src1, and perform W1Encode on w1 and store them into Keccak SIPO |
| NORM_CHK src, mode | Perform NormCheck on data at memory address src with mode configuration |
| SIG_ENCODE src0, src1, dest | Perform sigEncode on data at memory address src0 and src1 and store the results at register API address dest |
| DECOMP_SIGN src, dest | Perform Decompose on w data at memory address src and store w0 at memory address dest, and perform W1Encode on w1 and store them into Keccak SIPO |
| UPDATE_ΞΊ | The value of ΞΊ will be updated as ΞΊ+l |
| POWER2ROUND src, dest0, dest1 | Perform Power2Round on t data at memory address src and store t0 at register API address dest0 and t1 at register API address dest1 |
| SIG_DECODE_Z src, dest | Perform sigDecode_z on data at register API address src and store the results at memory address dest |
| SIG_DECODE_H src, dest | Perform sigDecode_h on data at register API address src and store the results at memory address dest |
Keygen Operation:
The algorithm for keygen is presented below. We will explain the specifics of each operation in the following subsections.

(p,p',K)= H(ΞΎ||K||L ,1024)
Keygen starts with running Keccak operation on seed to derive three different values. Seed is a value stored in register API, and we need to perform SHAKE256 with 256-bit inputs to generate 1024 bits output.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| (p,p',K)=Keccak(seed) | Keccak_SIPO | seed | 32 bytes | |
| Keccak_SIPO | K | 1 byte | ||
| Keccak_SIPO | L | 1 byte | ||
| Keccak_PISO | p | 32 bytes | ||
| Keccak_PISO | p' | 64 bytes | ||
| Keccak_PISO | K | 32 bytes |
Firstly, we need to fill Keccak input buffer with seed and then run the Keccak core. After that, the Keccak output stored in PISO is used to set the p, pβ, and K values.
(s1,s2)βExpandS(Οβ²)
We use the previous step's p' as the input for the Keccak and run the rejbounded sampler. For each polynomial, we need to feed the Keccak input buffer with p' and a constant value of length 16 bits. To do this, we first feed the 512-bit p' into SIPO, and then we add a 16 bits value (which acts as a counter from 0 to 14) to the end of the fed p' and then padding starts from there.
Rejbounded opcode enables both Keccak and sampler and shows the destination of output into the memory.
Then we run the rejbounded sampler 15 times with the shake256 mode. We can mask the latency of SIPO, the Keccak_SIPO can be invoked when rejbounded is handling the previous data. However, the Keccak will not be enabled until rejbounded is done.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| s1=expandS | Keccak_SIPO | p' | 0 (2bytes) | |
| rejbounded | s1_0 | |||
| Keccak_SIPO | p' | 1 | ||
| rejbounded | s1_1 | |||
| β¦ | ||||
| Keccak_SIPO | p' | 6 | ||
| rejbounded | s1_6 | |||
| s2=expandS | Keccak_SIPO | p' | 7 | |
| rejbounded | s2_0 | |||
| β¦ | ||||
| Keccak_SIPO | p' | 14 | ||
| rejbounded | s2_7 |
NTT(s1)
We need to call NTT for s1 by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(s1) | NTT | s1_0 | temp | s1_0_ntt |
| NTT | s1_1 | temp | s1_1_ntt | |
| β¦ | ||||
| NTT | s1_6 | temp | s1_6_ntt |
AΛ βExpandA(Ο) AND AΛ β¦NTT(s1)
We perform rejection sampling and PWM simultaneously. This step takes p from the first step and appends two bytes of Keccak SIPO to the end of the given p and then starts padding from there. We run the rejection sampler 56 times with shake128 mode, where k * l=56.
Each polynomial requires p and the necessary constants to fill SIPO. Then Rejection_sample opcode activates both Keccak and sampler. The output of rejection sampler goes straight to PWM unit. Then, the pwm opcode turns on pwm core, which can check the input from rejection sampler for a valid input.
There are two different opcodes for PWM: regular PWM and PWM_ACCU that indicates different modes for PWM units.
We can mask the latency of SIPO, the Keccak_SIPO can be invoked when PWM/Rejection_sampler is handling the previous data. However, the Keccak will not be enabled until PWM is done.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| As1_0=PWM(A, NTT(s1)) | Keccak_SIPO | p | 0 (1 byte) | 0 (1 byte) |
| Rejection_sampler | ||||
| pwm | DONTCARE | s1_0_ntt | As0 | |
| Keccak_SIPO | p | 0 | 1 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | s1_1_ntt | As0 | |
| Keccak_SIPO | p | 0 | 2 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | s1_2_ntt | As0 | |
| β¦ | ||||
| Keccak_SIPO | p | 0 | 6 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | s1_6_ntt | As0 | |
| As1_1=PWM(A, NTT(s1)) | Keccak_SIPO | p | 1 | 0 |
| Rejection_sampler | ||||
| pwm | DONTCARE | s1_0_ntt | As1 | |
| Keccak_SIPO | p | 1 | 1 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | s1_1_ntt | As1 | |
| β¦ | ||||
| Keccak_SIPO | p | 1 | 6 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | s1_6_ntt | As1 | |
| As1_7=PWM(A, NTT(s1)) | β¦ | |||
| Keccak_SIPO | p | 7 | 6 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | s1_6_ntt | As7 |
NTTβ1(AΛ β¦NTT(s1))
We need to call INTT for As1 by passing three addresses. Temp address can be the same for all INTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| INTT(As1) | INTT | As0 | temp | As0_intt |
| INTT | As1 | temp | As1_intt | |
| β¦ | ||||
| INTT | As7 | temp | As7_intt |
t βNTTβ1(AΛ β¦NTT(s1))+s2
We need to call point-wise addition for As1 results and s2 by passing three addresses.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| t=INTT(As1)+s2 | PWA | As0_intt | s2_0 | t0 |
| PWA | As1_intt | s2_1 | t1 | |
| β¦ | ||||
| PWA | As7_intt | s2_7 | t7 |
(t1,t0)βPower2Round(t,d) AND pk βpkEncode(Ο,t1) AND sk βskEncode(t0)
We need to call power2round for t results from the previous step, and the results are stored in two different addresses to API for sk and pk.
| Operation | Opcode | operand | operand | operand |
|---|---|---|---|---|
| (t1,t0)βPower2Round(t,d) | POWER2ROUND | t | t0 (sk) | t1 (pk) |
NOTE: The value of Ο needs to be also stored into register API for pk.
tr βH(BytesToBits(pk),512)
The value of pk from register API needs to be read and stored into Keccak SIPO to perform SHAKE256. Due to long size of pk, 20 rounds of Keccak is required to executed to generate 512-bit tr.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| tr=Keccak(pk) | Keccak_SIPO | pk | 2592 bytes | |
| Keccak_PISO | tr | 64 bytes |
sk βskEncode(Ο,K,tr,s1,s2,t0)
The value of Ο, K needs to be stored into register API, while tr and t0 are already stored by previous steps.
We need to call skEncode for s1 and s2 coefficients and the results are stored into API for sk.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| sk βskEncode(s1) | skEncode | s1 | sk | |
| sk βskEncode(s2) | skEncode | S2 | sk |
Signing
Signing operation is the most challenging operation for ML-DSA. From a high-level perspective, the required operation for performing a signing operation can be shown as follows:

The are some initial processes to decode private key. The loop between challenge generation and validity check should be continued until validity check passed the results and then, the signature will be generated and published.
In our optimized design, the next challenge is generated in advance, so that it can be used if the validity check of the current round fails. Therefore, we use two sequencers to support parallel operations.
The algorithm for signing is presented below. We will explain the specifics of each operation in the following subsections.

The following table shows the operations for each sequencer:
| Sequencer 1 | |
|---|---|
| Challenge Generation | ΞΌ βH(tr ||M,512) |
| Οβ²βH(K||rnd||ΞΌ,512) | |
| y βExpandMask(Οβ² ,ΞΊ) | |
| w βNTTβ1(AΛ β¦NTT(y)) | |
| (w1,w0) βDecompose(w) | |
| cΛβH(ΞΌ ||w1Encode(w1),2Ξ») | |
| c βSampleInBall(cΛ) | |
| ΞΊ βΞΊ +β |
| Sequencer 2 | |
|---|---|
| Initial steps | (Ο,K,tr,s1,s2,t0)βskDecode(sk) |
| sΛ1 βNTT(s1) | |
| sΛ2 βNTT(s2) | |
| Λt0 βNTT(t0) | |
| Validity Checks | cΛ βNTT(c) |
| \β¨\β¨cs1\β©\β©βNTTβ1(cΛβ¦ sΛ1) | |
| β¨β¨cs2β©β©βNTTβ1(cΛβ¦ sΛ2) | |
| z βy +β¨β¨cs1β©β© | |
| r0 β(w0 ββ¨β¨cs2β©β©) | |
| β¨β¨ct0β©β©βNTTβ1(cΛβ¦ tΛ0) | |
| h βMakeHint(w1, w0ββ¨β¨cs2β©β©+β¨β¨ct0β©β©) | |
| ||z||β β₯ Ξ³1 βΞ² | |
| ||r0 ||β β₯ Ξ³2 βΞ² | |
| ||β¨β¨ct0β©β©||β β₯ Ξ³2 | |
| Signature Generation | Ο βsigEncode(cΛ,z modΒ±q,h) |
(Ο,K,tr,s1,s2,t0)βskDecode(sk)
We need to call skDecode for s1, s2, and t0 by passing the required addresses.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| skDecode(s1) | skDecode | s1 | s1 | |
| skDecode(s2) | skDecode | s2 | s2 | |
| skDecode(t0) | skDecode | t0 | t0 |
sΛ1 βNTT(s1)
We need to call NTT for s1 by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(s1) | NTT | s1_0 | temp | s1_0_ntt |
| NTT | s1_1 | temp | s1_1_ntt | |
| β¦ | ||||
| NTT | s1_6 | temp | s1_6_ntt |
sΛ2 βNTT(s2)
We need to call NTT for t0 by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(s2) | NTT | t0_0 | temp | t0_0_ntt |
| NTT | t0_1 | temp | t0_1_ntt | |
| β¦ | ||||
| NTT | t0_7 | temp | t0_7_ntt |
Λt0 βNTT(t0)
We need to call NTT for s2 by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(s2) | NTT | s2_0 | temp | s2_0_ntt |
| NTT | s2_1 | temp | s2_1_ntt | |
| β¦ | ||||
| NTT | s2_7 | temp | s2_7_ntt |
cΛ βNTT(c)
This is the part where two sequencers sync up. Sequencer 1 will pause until the second sequencer finishes the SampleInBall operation and the ready flag from SampleInBall tells the first sequencer to go on.
We need to call NTT for c by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(c) | NTT | c | temp | c_ntt |
β¨β¨cs1β©β©βNTTβ1(cΛβ¦ sΛ1)
We need firstly to call PWM to perform point-wise multiplication between c and s1.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| cs1=PWM(c, s1) | PWM | c_ntt | s1_0_ntt | cs1_0_ntt |
| PWM | c_ntt | s1_1_ntt | cs1_1_ntt | |
| β¦ | ||||
| PWM | c_ntt | s1_6_ntt | cs1_6_ntt |
Then, we need to call INTT for cs1 by passing three addresses. Temp address can be the same for all INTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| INTT(cs1) | INTT | cs1_0_ntt | temp | cs1_0 |
| INTT | cs1_1_ntt | temp | cs1_1 | |
| β¦ | ||||
| INTT | cs1_6_ntt | temp | cs1_6 |
β¨β¨cs2β©β©βNTTβ1(cΛβ¦ sΛ2)
We need first to call PWM to perform point-wise multiplication between c and s2.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| cs2=PWM(c, s2) | PWM | c_ntt | s2_0_ntt | cs2_0_ntt |
| PWM | c_ntt | s2_1_ntt | cs2_1_ntt | |
| β¦ | ||||
| PWM | c_ntt | s2_7_ntt | cs2_7_ntt |
Then, we need to call INTT for cs2 by passing three addresses. Temp address can be the same for all INTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| INTT(cs2) | INTT | cs2_0_ntt | temp | cs2_0 |
| INTT | cs2_1_ntt | temp | cs2_1 | |
| β¦ | ||||
| INTT | cs2_7_ntt | temp | cs2_7 |
z βy +β¨β¨cs1β©β©
We need to call PWA to perform point-wise addition between cs1 and y.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| z=PWA(y, cs1) | PWA | y_0 | cs1_0 | z_0 |
| PWA | y_1 | cs1_1 | z_1 | |
| β¦ | ||||
| PWA | y_6 | cs1_6 | z_6 |
r0 β(w0 ββ¨β¨cs2β©β©)
We need to call PWS to perform point-wise subtraction between w0 and cs2.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| r0=PWS(w0, cs2) | PWS | w0_0 | cs2_0 | r0_0 |
| PWS | w0_1 | cs2_1 | r0_1 | |
| β¦ | ||||
| PWS | w0_7 | cs2_7 | r0_7 |
β¨β¨ct0β©β©βNTTβ1(cΛβ¦ tΛ0)
We need first to call PWM to perform point-wise multiplication between c and t0.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| ct0=PWM(c, t0) | PWM | c_ntt | t0_0_ntt | ct0_0_ntt |
| PWM | c_ntt | t0_1_ntt | ct0_1_ntt | |
| β¦ | ||||
| PWM | c_ntt | t0_7_ntt | ct0_7_ntt |
Then, we need to call INTT for ct0 by passing three addresses. Temp address can be the same for all INTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| INTT(ct0) | INTT | ct0_0_ntt | temp | ct0_0 |
| INTT | ct0_1_ntt | temp | ct0_1 | |
| β¦ | ||||
| INTT | ct0_7_ntt | temp | ct0_7 |
h βMakeHint(w1,r0+β¨β¨ct0β©β©)
We need to call PWA to perform point-wise addition between r0 and ct0.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| hint_r=PWA(r0, ct0) | PWA | hint_r_0 | r0_0 | ct0_0 |
| PWA | hint_r_1 | r0_1 | ct0_1 | |
| β¦ | ||||
| PWA | hint_r_7 | r0_7 | ct0_7 |
Then, we need to call MakeHint for hint_r. It will process the entire hint_r polynomials, i.e., from hint_r_0 to hint_r_7.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| hint_r=PWA(r0, ct0) | MAKEHINT | hint_r_0 | h |
||z||β β₯ Ξ³1 βΞ²
We need to call Norm_Check to perform validity check on z. The output will be stored as an individual flag in the high-level architecture.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Valid=NormCheck(z) | NormChk | z | mode |
||r0||β β₯ Ξ³2 βΞ²
We need to call Norm_Check to perform validity check on r0. The output will be stored as an individual flag in the high-level architecture.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Valid=NormCheck(r0) | NormChk | r | mode |
||β¨β¨ct0β©β©||β β₯ Ξ³2
We need to call Norm_Check to perform validity check on ct0. The output will be stored as an individual flag in the high-level architecture.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Valid=NormCheck(ct0) | NormChk | ct0 | mode |
Ο βsigEncode(cΛ,z modΒ±q,h)
This step writes the final signature into the register API. Before that, the high-level design will check all four flags coming from:
- MakeHint
- NormChk on z
- NormChk on r0
- NormChk on ct0
If all checks show the successful signature, then the sigEncode unit will be called. The value of h is already stored by MakeHint unit into the register API. Hence, only two remaining parts will be passed to this unit.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Ο=sigEncode(c,z) | sigEncode | c | z | Ο |
ΞΌ βH(tr||M,512)
The other sequencer starts with running Keccak operation on tr and the given message. tr and the message are stored in register API as inputs, and we need to perform SHAKE256 with to generate 512 bits output.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| ΞΌ=Keccak(tr|| M) | Keccak_SIPO | tr | 64 bytes | |
| Keccak_SIPO | 0 | 2 bytes | ||
| Keccak_SIPO | Message | 64 bytes | ||
| Keccak_PISO | ΞΌ | 64 bytes |
Οβ²βH(K||rnd||ΞΌ,512)
We need to run Keccak operation on K, rnd, and ΞΌ values. K and rnd are stored in register API as inputs, and ΞΌ is stored in an internal register. we need to perform SHAKE256 with to generate 512 bits output.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Οβ²=Keccak(K || rnd || ΞΌ) | Keccak_SIPO | K | 4 (x64 words) | |
| Keccak_SIPO | sign_rnd | 4 (x64 words) | ||
| Keccak_SIPO | ΞΌ | 8 (x64 words) | ||
| Keccak_PISO | Οβ² | 8 (x64 words) |
Firstly, we need to fill Keccak input buffer with K and then concatenate it with sign_rnd and ΞΌ. Then we run the Keccak core, and the Keccak output stored in PISO is used to set the Οβ² value into a special register.
y βExpandMask(Οβ ,ΞΊ)
We use the previous step's p' as the input for the Keccak and run the ExpandMask sampler. For each polynomial, we need to feed the Keccak input buffer with p' and a register value of length 16 bits. To do this, we first feed the 512-bit p' into SIPO, and then we add a 16 bits value (which acts as a counter from 0 to 6) to the end of the fed p' and then padding starts from there.
ExpandMask opcode enables both Keccak and sampler and shows the destination of output into the memory.
Then we run the ExpandMask sampler 7 times with the shake256 mode. We can mask the latency of SIPO, the Keccak_SIPO can be invoked when ExpandMask is handling the previous data. However, the Keccak will not be enabled until ExpandMask is done.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| y=ExpandMask(Οβ ,ΞΊ) | LDKeccak | p' | 64 bytes | |
| LDKeccak | ΞΊ | 2 bytes | ||
| Exp_Mask | y_0 | |||
| LDKeccak | p' | 1 bytes | ||
| LDKeccak | ΞΊ+1 | 2 bytes | ||
| Exp_Mask | y_1 | |||
| β¦ | ||||
| LDKeccak | p' | 64 bytes | ||
| LDKeccak | ΞΊ +6 | 2 bytes | ||
| Exp_Mask | y_6 |
NTT(y)
We need to call NTT for s1 by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(y) | NTT | y_0 | temp | y_0_ntt |
| NTT | y_1 | temp | y_1_ntt | |
| β¦ | ||||
| NTT | y_6 | temp | y_6_ntt |
AΛ βExpandA(Ο) AND AΛ β¦NTT(y)
We perform rejection sampling and PWM simultaneously. This step takes p from and appends two bytes of Keccak SIPO to the end of the given p and then starts padding from there. We run the rejection sampler 56 times with shake128 mode, where k * l=56.
Each polynomial requires p and the necessary constants to fill SIPO. Then Rejection_sample opcode activates both Keccak and sampler. The output of rejection sampler goes straight to PWM unit. Then, the pwm opcode turns on pwm core, which can check the input from rejection sampler for a valid input.
There are two different opcodes for PWM: regular PWM and PWM_ACCU that indicates different modes for PWM units.
We can mask the latency of SIPO, the Keccak_SIPO can be invoked when PWM/Rejection_sampler is handling the previous data. However, the Keccak will not be enabled until PWM is done.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Ay0=PWM(A, NTT(y)) | LDKeccak | p | 32 bytes | |
| LDKeccak | 0 | 1 byte | ||
| LDKeccak | 0 | 1 byte | ||
| Rejection_sampler | ||||
| pwm | DONTCARE | y_0_ntt | Ay0 | |
| LDKeccak | p | 32 bytes | ||
| LDKeccak | 0 | 1 byte | ||
| LDKeccak | 1 | 1 byte | ||
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | y_1_ntt | Ay0 | |
| LDKeccak | p | 32 bytes | ||
| LDKeccak | 0 | 1 byte | ||
| LDKeccak | 2 | 1 byte | ||
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | y_2_ntt | Ay0 | |
| β¦ | ||||
| LDKeccak | p | 32 bytes | ||
| LDKeccak | 0 | 1 byte | ||
| LDKeccak | 6 | 1 byte | ||
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | y_6_ntt | Ay0 | |
| Ay1=PWM(A, NTT(y)) | LDKeccak | p | 32 bytes | 0 |
| LDKeccak | 1 | 1 byte | ||
| LDKeccak | 0 | 1 byte | ||
| Rejection_sampler | ||||
| pwm | DONTCARE | y_0_ntt | Ay1 | |
| LDKeccak | p | 32 bytes | ||
| LDKeccak | 1 | 1 byte | ||
| LDKeccak | 1 | 1 byte | ||
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | y_1_ntt | Ay1 | |
| β¦ | ||||
| LDKeccak | p | 32 bytes | ||
| LDKeccak | 1 | 1 byte | ||
| LDKeccak | 6 | 1 byte | ||
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | y_6_ntt | Ay1 | |
| Ay7=PWM(A, NTT(y)) | β¦ | |||
| LDKeccak | p | 32 bytes | ||
| LDKeccak | 7 | 1 byte | ||
| LDKeccak | 6 | 1 byte | ||
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | y_6_ntt | Ay7 |
w βNTTβ1(AΛ β¦NTT(y))
We need to call INTT for Ay by passing three addresses. Temp address can be the same for all INTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| INTT(Ay) | INTT | Ay0 | temp | w_0 |
| INTT | Ay1 | temp | w_1 | |
| β¦ | ||||
| INTT | Ay7 | temp | w_7 |
(w1,w0) βDecompose(w) AND cΛβH(ΞΌ||w1Encode(w1),2Ξ»)
The decompose unit takes w from memory and splits it into two parts. It saves w0 in memory and sends w1 to the Keccak SIPO for SampleInBall. However, SIPO requires the ΞΌ prefix before receiving the w1 values. Therefore, the high-level controller should provide ΞΌ before using decompose. After completing the decompose operation, the high-level controller needs to add the necessary padding for H(ΞΌ||w1Encode(w1),2Ξ»). Then, by activating the SampleInBall, the Keccak will start and the data in the SIPO will be processed.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| H(ΞΌ || w1Encode(w1),2Ξ») | LDKeccak | ΞΌ | 64 bytes | |
| (w1,w0) βDecompose(w) | Decomp_Enc | w | w0 | |
| H(ΞΌ | w1Encode(w1),2Ξ») | LDKeccak | padding |
c βSampleInBall(cΛ)
We take the SIPO value from the last step as the Keccak input and run SampleInBall. The output stays in the SampleInBall memory.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| c βSampleInBall(cΛ1) | SMPL_INBALL |
ΞΊ βΞΊ +β
High-level controller increases the value of ΞΊ by l.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| ΞΊ βΞΊ +β | Update_k |
Verifying
The algorithm for verifying is presented below. We will explain the specifics of each operation in the following subsections.

(Ο,t1)βpkDecode(pk)
We need to call pkDecode to decode the given pk for t1 values.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| t1βpkDecode(pk) | pkDecode | pk | t1 |
(cΛ,z,h)βsigDecode(Ο)
We need to call sigDecode to decode the given signature for z and h values.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| (z,h)βsigDecode(Ο) | sigDecode_z | Ο_z | z | |
| sigDecode_h | Ο_h | h |
||z||β β₯ Ξ³1 βΞ²
We need to call Norm_Check to perform validity check on the given z. The output will be stored as an individual flag in the high-level architecture.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Valid=NormCheck(z) | NormChk | z | mode |
[[number of 1βs in h is β€ Ο]]
We need to call HintSum to perform validity check on the given h. The output will be stored as an individual flag in the high-level architecture.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Valid=HintSum(h) | HINTSUM | h |
z βNTT(z)
We need to call NTT for z by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(z) | NTT | z_0 | temp | z_0_ntt |
| NTT | z_1 | temp | z_1_ntt | |
| β¦ | ||||
| NTT | z_6 | temp | z_6_ntt |
AΛ βExpandA(Ο) AND AΛ β¦NTT(z)
We perform rejection sampling and PWM simultaneously. This step takes p from the register API and appends two bytes of Keccak SIPO to the end of the given p and then starts padding from there. We run the rejection sampler 56 times with shake128 mode, where k * l=56.
Each polynomial requires p and the necessary constants to fill SIPO. Then Rejection_sample opcode activates both Keccak and sampler. The output of rejection sampler goes straight to PWM unit. Then, the pwm opcode turns on pwm core, which can check the input from rejection sampler for a valid input.
There are two different opcodes for PWM: regular PWM and PWM_ACCU that indicates different modes for PWM units.
We can mask the latency of SIPO, the Keccak_SIPO can be invoked when PWM/Rejection_sampler is handling the previous data. However, the Keccak will not be enabled until PWM is done.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Az_0=PWM(A, NTT(z)) | Keccak_SIPO | p | 0 (1 byte) | 0 (1 byte) |
| Rejection_sampler | ||||
| pwm | DONTCARE | z_0_ntt | Az0 | |
| Keccak_SIPO | p | 0 | 1 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | z_1_ntt | Az0 | |
| Keccak_SIPO | p | 0 | 2 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | z_2_ntt | Az0 | |
| β¦ | ||||
| Keccak_SIPO | p | 0 | 6 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | z_6_ntt | Az0 | |
| Az_1=PWM(A, NTT(z)) | Keccak_SIPO | p | 1 | 0 |
| Rejection_sampler | ||||
| pwm | DONTCARE | z_0_ntt | Az1 | |
| Keccak_SIPO | p | 1 | 1 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | z_1_ntt | Az1 | |
| β¦ | ||||
| Keccak_SIPO | p | 1 | 6 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | z_6_ntt | Az1 | |
| Az_7=PWM(A, NTT(z)) | β¦ | |||
| Keccak_SIPO | p | 7 | 6 | |
| Rejection_sampler | ||||
| pwm_accu | DONTCARE | z_6_ntt | Az7 |
tr βH(pk,512)
The sequencer runs Keccak operation on pk. pk is stored in register API as input, and we need to perform SHAKE256 with to generate 512 bits output.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| tr=Keccak(pk) | Keccak_SIPO | pk | 2592 bytes | |
| Keccak_PISO | tr | 64 bytes |
ΞΌ βH(tr||M,512)
The sequencer starts with running Keccak operation on tr and the given message. tr is stored in an internal register from the previous step, and the message is stored in register API as input, and we need to perform SHAKE256 with to generate 512 bits output.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| ΞΌ=Keccak(tr || M) | Keccak_SIPO | tr | 64 bytes | |
| Keccak_SIPO | 0 | 2 bytes | ||
| Keccak_SIPO | Message | 64 bytes | ||
| Keccak_PISO | ΞΌ | 64 bytes |
c βSampleInBall(cΛ)
We take the c~ values from register API as the Keccak input and run SampleInBall. The output stays in the SampleInBall memory.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Keccak_SIPO | c~ | 64 bytes | ||
| c βSampleInBall(cΛ) | SMPL_INBALL |
cΛ βNTT(c)
We need to call NTT for c by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(c) | NTT | c | temp | c_ntt |
cΛ βNTT(c)
We need to call NTT for c by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(c) | NTT | c | temp | c_ntt |
t1 βNTT(t1)
We need to call NTT for t1 by passing three addresses. Temp address can be the same for all NTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| NTT(t1) | NTT | t1_0 | temp | t1_0_ntt |
| NTT | t1_1 | temp | t1_1_ntt | |
| β¦ | ||||
| NTT | t1_7 | temp | t1_7_ntt |
NTT(c) β¦NTT(t1)
We need to call point-wise multiplication between c and all t1 polynomials in NTT domain.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| ct1=PWM(NTT(c) β¦NTT(t1)) | PWM | c_ntt | t1_0_ntt | ct1_0 |
| PWM | c_ntt | t1_1_ntt | ct1_1 | |
| β¦ | ||||
| . | PWM | c_ntt | t1_7_ntt | ct1_7 |
A Λ β¦NTT(z*)* βNTT(c) β¦NTT(t1)
We need to call point-wise subtraction between Az and ct1 polynomials in NTT domain.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| Az-ct1=A Λ β¦NTT(z*)* βNTT(c) β¦NTT(t1) | PWS | Az_0 | ct1_0 | Az_ct1_0 |
| PWS | Az_1 | ct1_1 | Az_ct1_1 | |
| β¦ | ||||
| . | PWS | Az_7 | ct1_7 | Az_ct1_7 |
wβ² βNTT-1(A Λ β¦NTT(z*)* βNTT(c) β¦NTT(t1))
We need to call INTT for Az_ct1 by passing three addresses. Temp address can be the same for all INTT calls while init and destination are different.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| wβ² βNTT-1(A Λ β¦NTT(z*)* βNTT(c) β¦NTT(t1)) | INTT | Az_ct1_0 | temp | wβ_0 |
| INTT | Az_ct1_1 | temp | wβ_1 | |
| β¦ | ||||
| INTT | Az_ct1_7 | temp | wβ_7 |
w β² βUseHint(h,w β²) AND cΛβH(ΞΌ||w1Encode(w1),2Ξ»)
In the UseHint phase, the decompose unit retrieves w from memory and divides it into two components. Next, w1 is refreshed through useHint, encoded, and forwarded to the Keccak SIPO. Nonetheless, the ΞΌ prefix must precede w1 before SIPO can accept it. Therefore, the high-level controller should provide ΞΌ before using decompose. After completing the UseHint operation, the high-level controller needs to add the necessary padding for H(ΞΌ||w1Encode(w1),2Ξ»). Then, the Keccak will start and the data in the SIPO will be stored at register API as verification result.
| Operation | opcode | operand | operand | operand |
|---|---|---|---|---|
| H(ΞΌ || w1Encode(w1),2Ξ») | LDKeccak | ΞΌ | 64 bytes | |
| w β² βUseHint(h,w β²) | USEHINT | w | H | |
| H(ΞΌ || w1Encode(w1),2Ξ») | LDKeccak | padding | ||
| EN_Keccak | ||||
| RDKeccak | Verification Result |
References:
[1] The White House, "National Security Memorandum on Promoting United States Leadership in Quantum Computing While Mitigating Risks to Vulnerable Cryptographic Systems," 2022. [Online]. Available: White House.
[2] NIST, "PQC Standardization Process: Announcing Four Candidates to be Standardized, Plus Fourth Round Candidates," [Online]. Available: NIST PQC. [Accessed 2022].
[3] NIST, "FIPS 204 Module-Lattice-Based Digital Signature Standard," August 13, 2024.
a2c404a

Adam's Bridge ML-KEM Hardware Specification
Version 1.0
ML-KEM Overview
ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism) is a quantum-resistant Key exchange scheme defined in FIPS 203 [1].
High-Level Overview
Adamβs Bridge MK-KEM accelerator has all the necessary components to execute a pure hardware PQC operation. The main operations that involve more computational complexity, such as NTT, hashing, and sampling units, are explained as follows.

The security level of ML-KEM defined by NIST are as follows:
| Algorithm Name | Security Level |
|---|---|
| ML-KEM-512 | Level-1 |
| ML-KEM-768 | Level-3 |
| ML-KEM-1024 | Level-5 |
CNSA 2.0 only allows the highest security level (Level-5) for PQC which is ML-KEM-1024, and Adams Bridge only supports ML-KEM-1024 parameter set.
API
TheΒ ML-KEM-1024Β architectureΒ inputsΒ andΒ outputsΒ areΒ describedΒ inΒ theΒ followingΒ table.
| Name | Input/Output | Operation | Size (Byte) |
|---|---|---|---|
| name | Output | All | 8 |
| version | Output | All | 8 |
| ctrl | Input | All | 4 |
| status | Output | All | 4 |
| entropy (SCA) | Input | All | 64 |
| seed_d | Input | Keygen | 32 |
| seed_z | Input | Keygen | 32 |
| message | Input | Encaps | 32 |
| shared_key | Output | Encaps/Decaps | 32 |
| decaps_key | Input/Output | Keygen/Decaps | 3168 |
| encaps_key | Input/Output | Keygen/Decaps | 1568 |
| ciphertext | Input/Output | Encaps/Decaps | 1568 |
| Interrupt | Output | All | 520 |
| --------------------------- | --------------- | --------------- | ------------- |
| Total | 7040 |
name
βRead-only register consists of the name of component.Β
versionΒ
βRead-only register consists of the version of component.Β
CTRLΒ
βThe control register consists of the following flags:Β
| Bits | Identifier | Access | Reset | Decoded | Name |
|---|---|---|---|---|---|
| [31:4] | - | - | - | - | |
| [3] | ZEROIZE | w | 0x0 | - | |
| [2:0] | CTRL | w | 0x0 | - |
βCTRLΒ
CTRL command field contains two bits indicating:
- βCtrl = 0b000Β
βNo Operation.Β
- βCtrl = 0b001Β
βTrigs the core to start the initialization and perform keygen operation.Β
- βCtrl = 0b010Β
βTrigs the core to start the Encapsulation operation.
- βCtrl = 0b011Β
βTrigs the core to start Decapsulation operation.
- Ctrl = 0b100Β
βTrigs the core to start the keygen+Decapsulation operation for a ciphertext block. Β This mode decreases storage costs for the decaps_key (dk) by recalling keygen and using an on-the-fly decaps_key during the decapsultion process.
ZEROIZE
Zeroize all internal registers: Zeroize all internal registers after process to avoid SCA leakage.
Software write generates only a single-cycle pulse on the hardware interface and then will be erased.
statusΒ
βThe read-only status register consists of the following flags:Β
| Bits | Identifier | Access | Reset | Decoded | Name |
|---|---|---|---|---|---|
| [31:2] | - | - | - | - | |
| [2] | ERROR | r | 0x0 | - | |
| [1] | VALID | r | 0x0 | - | |
| [0] | READY | r | 0x0 | - |
READYΒ
βIndicates if the core is ready to process the inputs.Β
βVALIDΒ
βIndicates if the process is computed and the output is valid.Β
ERROR
βIndicates if the process could not complete due to an error. For encapsulation, this indicates that the provided encapsulation key does not pass the FIPS 203 input check (Modulus check). For decapsulation, this indicates that the provided decapsulation key does not pass the FIPS 203 input check (Hash check).
entropy
Entropy is required for SCA countermeasures to randomize the inputs with no change in the outputs. The entropy can be any 512-bit value in [0 : 2^512-1].Β
TheΒ ML-KEM-1024 countermeasure requires several random vectors to randomize the intermediate values. An internal mechanism is considered to take one random vector of 512-bit (i.e., entropy register) and generate the required random vectors for different countermeasures.
seed_d
Adams Bridge component seed_d register type definition 8 32-bit registers storing the 256-bit seed_d for keygen. The seed can be any 256-bit value in [0 : 2^256-1].
seed_z
Adams Bridge component seed_z register type definition 8 32-bit registers storing the 256-bit seed_z for keygen. The seed can be any 256-bit value in [0 : 2^256-1].
message
Adams Bridge component message register type definition 8 32-bit registers storing the 256-bit message for encapsulation. The message can be any 256-bit value in [0 : 2^256-1].
shared_key
This register stores the shared_key for generated by encapsulation or decapsulation operations.
decaps_key
This register stores the decapsulation key generated in keygen. This register should be set before decapsulation operation.
If seed_d or seed_z comes from key vault, this register will not contain the decaps_key to avoid exposing secret assets to software.
encaps_key
This register stores the encapsulation key generated in keygen. This register should be set before encapsulation operation.
ciphertext
This register stores the ciphertext generated in encapsulation operation. This register should be set before decapsulation operation.
βPseudocodeΒ
βKeygenΒ
Input:
seed_d
seed_z
entropy
Output:
encaps_key
decaps_key
// Wait for the core to be ready (STATUS flag should be 2'b01 or 2'b11)
read_data = 0
while read_data == 0:
read_data = read(ADDR_STATUS)
// Feed the required inputs
write(ADDR_SEED_D, seed_d)
write(ADDR_SEED_Z, seed_z)
write(ADDR_ENTROPY, entropy)
// Trigger the core for performing Keygen
write(ADDR_CTRL, KEYGEN_CMD) // (STATUS flag will be changed to 2'b00)
// Wait for the core to be ready and valid (STATUS flag should be 2'b11)
read_data = 0
while read_data == 0:
read_data = read(ADDR_STATUS)
// Reading the outputs
encaps_key = read(ADDR_EK)
decaps_key = read(ADDR_DK)
// Return the outputs
return encaps_key, decaps_key
βΒ
Encapsulation
βInput:
msg
encaps_key
entropy
Output:
shared_key
ciphertext
// Wait for the core to be ready (STATUS flag should be 2'b01 or 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Feed the required inputs
write(ADDR_MSG, msg);
write(ADDR_EK, encaps_key);
write(ADDR_ENTROPY, entropy);
// Trigger the core for performing Encapsulation
write(ADDR_CTRL, ENCAPS_CMD); // (STATUS flag will be changed to 2'b00)
// Wait for the core to be ready and valid (STATUS flag should be 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Reading the outputs
shared_key = read(ADDR_SHAREDKEY);
ciphertext = read(ADDR_CIPHERTEXT);
// Return the output
return shared_key, ciphertext;
Decapsulation
Input:
decaps_key
ciphertext
entropy
Output:
shared_key
// Wait for the core to be ready (STATUS flag should be 2'b01 or 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Feed the required inputs
write(ADDR_DK, decaps_key);
write(ADDR_CIPHERTEXT, ciphertext);
write(ADDR_ENTROPY, entropy);
// Trigger the core for performing decapsulation
write(ADDR_CTRL, DECAPS_CMD); // (STATUS flag will be changed to 2'b00)
// Wait for the core to be ready and valid (STATUS flag should be 2'b11)
read_data = 0;
while (read_data == 0) {
read_data = read(ADDR_STATUS);
}
// Reading the output
shared_key = read(ADDR_SHAREDKEY);
// Return the output
return shared_key;
Keygen + Decapsulation
This mode decreases storage costs for the decaps_key (DK) by recalling keygen and using an on-the-fly decaps_key during the decapsulaiton process.
Input:
seed_d
seed_z
ciphertext
entropy
Output:
shared_key
// Wait for the core to be ready (STATUS flag should be 2'b01 or 2'b11)
read_data = 0
while read_data == 0:
read_data = read(ADDR_STATUS)
// Feed the required inputs
write(ADDR_SEED_D, seed_d)
write(ADDR_SEED_Z, seed_z)
write(ADDR_CIPHERTEXT, ciphertext);
write(ADDR_ENTROPY, entropy)
// Trigger the core for performing Keygen + Decapsulation
write(ADDR_CTRL, KEYGEN_DECAPS_CMD) // (STATUS flag will be changed to 2'b00)
// Wait for the core to be ready and valid (STATUS flag should be 2'b11)
read_data = 0
while read_data == 0:
read_data = read(ADDR_STATUS)
// Reading the output
shared_key = read(ADDR_SHAREDKEY);
// Return the outputs
return shared_key;
βΒ
Performance and Area Results
ML-KEM-1024
TBD
NTT/INTT
Number Theoretic Transform (NTT) and inverse Number Theoretic Transform (INTT) are used to achieve more efficient polynomial multiplication in lattice-based cryptosystems by reducing time-complexity from O(n2) to O(n log n).
NTT/INTT computation is made up of many butterfly operations. A butterfly operation is a mathematical operation that merges two coefficients to produce two outputs to help with NTT and INTT calculation. The NTT engine consists of 2 stages of 2 butterfly units connected serially which results in a 2x2 architecture. This is a pipelined architecture where every clock cycle, each butterfly unit receives two input coefficients and produces two output coefficients.
As a result of this pipelined 2x2 architecture, even number of stages are executed to complete a single NTT/INTT operation. However, in ML-KEM definition of NTT/INTT, (log n)-1 layers of NTT operation must be performed, where n is the number of coefficients in the polynomial. For n = 256, 7 layers of computation must be done to complete one NTT/INTT operation.
This mismatch between the required odd number of layers in ML-KEM and the fixed even-layered execution of a conventional pipelined NTT/INTT engine creates a fundamental challenge. ML-KEM employs an even/odd coefficient separation technique, where each butterfly operation processes pairs of coefficients that are either both even-indexed or both odd-indexed in the polynomial. This structure disrupts the standard sequential memory access pattern used in conventional NTT architectures, as coefficients that should be processed together are no longer contiguous in memory. As a result, direct reuse of a standard 2x2 NTT pipeline leads to inefficient memory access, increased data shuffling, and potential performance bottlenecks. Addressing this requires modifications to the memory access strategy and data flow to ensure that the ML-KEM NTT can be efficiently computed without excessive latency or resource overhead.
We propose an efficient method to achieve the incomplete NTT/INTT computation without any area, memory or latency overhead and without compromising the arithmetic correctness of the algorithm.
- NTT memory access resolution for ML-KEM
In ML-KEM, for n=256 coefficients, (log n)-1 layers of NTT/INTT operations must be performed, where even and odd coefficients are grouped together and computed separately. The twiddle factors must be reused within even and odd computations.
Consider the following memory layout as an example output of stage 1&2 of NTT:
| 1&2 | |||
|---|---|---|---|
| 0 | 64 | 128 | 192 |
| 1 | 65 | 129 | 193 |
| 2 | 66 | 130 | 194 |
| 3 | 67 | 131 | 195 |
| 4 | 68 | 132 | 196 |
| 5 | 69 | 133 | 197 |
| 6 | 70 | 134 | 198 |
| 7 | 71 | 135 | 199 |
| 8 | 72 | 136 | 200 |
| 9 | 73 | 137 | 201 |
| 10 | 74 | 138 | 202 |
| 11 | 75 | 139 | 203 |
| 12 | 76 | 140 | 204 |
| 13 | 77 | 141 | 205 |
| 14 | 78 | 142 | 206 |
| 15 | 79 | 143 | 207 |
| 16 | 80 | 144 | 208 |
| 17 | 81 | 145 | 209 |
| 18 | 82 | 146 | 210 |
| 19 | 83 | 147 | 211 |
| 20 | 84 | 148 | 212 |
| 21 | 85 | 149 | 213 |
| 22 | 86 | 150 | 214 |
| 23 | 87 | 151 | 215 |
| 24 | 88 | 152 | 216 |
| 25 | 89 | 153 | 217 |
| 26 | 90 | 154 | 218 |
| 27 | 91 | 155 | 219 |
| 28 | 92 | 156 | 220 |
| 29 | 93 | 157 | 221 |
| 30 | 94 | 158 | 222 |
| 31 | 95 | 159 | 223 |
| 32 | 96 | 160 | 224 |
| 33 | 97 | 161 | 225 |
| 34 | 98 | 162 | 226 |
| 35 | 99 | 163 | 227 |
| 36 | 100 | 164 | 228 |
| 37 | 101 | 165 | 229 |
| 38 | 102 | 166 | 230 |
| 39 | 103 | 167 | 231 |
| 40 | 104 | 168 | 232 |
| 41 | 105 | 169 | 233 |
| 42 | 106 | 170 | 234 |
| 43 | 107 | 171 | 235 |
| 44 | 108 | 172 | 236 |
| 45 | 109 | 173 | 237 |
| 46 | 110 | 174 | 238 |
| 47 | 111 | 175 | 239 |
| 48 | 112 | 176 | 240 |
| 49 | 113 | 177 | 241 |
| 50 | 114 | 178 | 242 |
| 51 | 115 | 179 | 243 |
| 52 | 116 | 180 | 244 |
| 53 | 117 | 181 | 245 |
| 54 | 118 | 182 | 246 |
| 55 | 119 | 183 | 247 |
| 56 | 120 | 184 | 248 |
| 57 | 121 | 185 | 249 |
| 58 | 122 | 186 | 250 |
| 59 | 123 | 187 | 251 |
| 60 | 124 | 188 | 252 |
| 61 | 125 | 189 | 253 |
| 62 | 126 | 190 | 254 |
| 63 | 127 | 191 | 255 |
The following coefficient groups are executed by the 2x2 butterfly architecture:
Cycle 0 β {0, 64, 128, 192}
Cycle 1 β {1, 65, 129, 193}
Cycle 2 β {2, 66, 130, 194}
Cycle 3 β {3, 67, 131, 195}, etc
Every cycle, the coefficients toggle between even and odd. By managing twiddle factor input such that the same value is held for the next clock cycle, the NTT can perform one even followed by one odd operation before moving onto the next set of coefficients.
Following memory layout shows output of stage 3&4:
| 3&4 | |||
|---|---|---|---|
| 0 | 16 | 32 | 48 |
| 64 | 80 | 96 | 112 |
| 128 | 144 | 160 | 176 |
| 192 | 208 | 224 | 240 |
| 1 | 17 | 33 | 49 |
| 65 | 81 | 97 | 113 |
| 129 | 145 | 161 | 177 |
| 193 | 209 | 225 | 241 |
| 2 | 18 | 34 | 50 |
| 66 | 82 | 98 | 114 |
| 130 | 146 | 162 | 178 |
| 194 | 210 | 226 | 242 |
| 3 | 19 | 35 | 51 |
| 67 | 83 | 99 | 115 |
| 131 | 147 | 163 | 179 |
| 195 | 211 | 227 | 243 |
| 4 | 20 | 36 | 52 |
| 68 | 84 | 100 | 116 |
| 132 | 148 | 164 | 180 |
| 196 | 212 | 228 | 244 |
| 5 | 21 | 37 | 53 |
| 69 | 85 | 101 | 117 |
| 133 | 149 | 165 | 181 |
| 197 | 213 | 229 | 245 |
| 6 | 22 | 38 | 54 |
| 70 | 86 | 102 | 118 |
| 134 | 150 | 166 | 182 |
| 198 | 214 | 230 | 246 |
| 7 | 23 | 39 | 55 |
| 71 | 87 | 103 | 119 |
| 135 | 151 | 167 | 183 |
| 199 | 215 | 231 | 247 |
| 8 | 24 | 40 | 56 |
| 72 | 88 | 104 | 120 |
| 136 | 152 | 168 | 184 |
| 200 | 216 | 232 | 248 |
| 9 | 25 | 41 | 57 |
| 73 | 89 | 105 | 121 |
| 137 | 153 | 169 | 185 |
| 201 | 217 | 233 | 249 |
| 10 | 26 | 42 | 58 |
| 74 | 90 | 106 | 122 |
| 138 | 154 | 170 | 186 |
| 202 | 218 | 234 | 250 |
| 11 | 27 | 43 | 59 |
| 75 | 91 | 107 | 123 |
| 139 | 155 | 171 | 187 |
| 203 | 219 | 235 | 251 |
| 12 | 28 | 44 | 60 |
| 76 | 92 | 108 | 124 |
| 140 | 156 | 172 | 188 |
| 204 | 220 | 236 | 252 |
| 13 | 29 | 45 | 61 |
| 77 | 93 | 109 | 125 |
| 141 | 157 | 173 | 189 |
| 205 | 221 | 237 | 253 |
| 14 | 30 | 46 | 62 |
| 78 | 94 | 110 | 126 |
| 142 | 158 | 174 | 190 |
| 206 | 222 | 238 | 254 |
| 15 | 31 | 47 | 63 |
| 79 | 95 | 111 | 127 |
| 143 | 159 | 175 | 191 |
| 207 | 223 | 239 | 255 |
As shown, in merged stages 3&4, even operations occur for 4 consecutive cycles and followed by 4 odd operations for next 4 consecutive cycles. This behavior continues for entire polynomial. By repeating the twiddle factor input between every 4 cycles, the entire polynomial can be processed efficiently by the NTT engine.
In stage 5&6, the memory output is as follows:
| 5&6 | |||
|---|---|---|---|
| 0 | 4 | 8 | 12 |
| 16 | 20 | 24 | 28 |
| 32 | 36 | 40 | 44 |
| 48 | 52 | 56 | 60 |
| 64 | 68 | 72 | 76 |
| 80 | 84 | 88 | 92 |
| 96 | 100 | 104 | 108 |
| 112 | 116 | 120 | 124 |
| 128 | 132 | 136 | 140 |
| 144 | 148 | 152 | 156 |
| 160 | 164 | 168 | 172 |
| 176 | 180 | 184 | 188 |
| 192 | 196 | 200 | 204 |
| 208 | 212 | 216 | 220 |
| 224 | 228 | 232 | 236 |
| 240 | 244 | 248 | 252 |
| 1 | 5 | 9 | 13 |
| 17 | 21 | 25 | 29 |
| 33 | 37 | 41 | 45 |
| 49 | 53 | 57 | 61 |
| 65 | 69 | 73 | 77 |
| 81 | 85 | 89 | 93 |
| 97 | 101 | 105 | 109 |
| 113 | 117 | 121 | 125 |
| 129 | 133 | 137 | 141 |
| 145 | 149 | 153 | 157 |
| 161 | 165 | 169 | 173 |
| 177 | 181 | 185 | 189 |
| 193 | 197 | 201 | 205 |
| 209 | 213 | 217 | 221 |
| 225 | 229 | 233 | 237 |
| 241 | 245 | 249 | 253 |
| 2 | 6 | 10 | 14 |
| 18 | 22 | 26 | 30 |
| 34 | 38 | 42 | 46 |
| 50 | 54 | 58 | 62 |
| 66 | 70 | 74 | 78 |
| 82 | 86 | 90 | 94 |
| 98 | 102 | 106 | 110 |
| 114 | 118 | 122 | 126 |
| 130 | 134 | 138 | 142 |
| 146 | 150 | 154 | 158 |
| 162 | 166 | 170 | 174 |
| 178 | 182 | 186 | 190 |
| 194 | 198 | 202 | 206 |
| 210 | 214 | 218 | 222 |
| 226 | 230 | 234 | 238 |
| 242 | 246 | 250 | 254 |
| 3 | 7 | 11 | 15 |
| 19 | 23 | 27 | 31 |
| 35 | 39 | 43 | 47 |
| 51 | 55 | 59 | 63 |
| 67 | 71 | 75 | 79 |
| 83 | 87 | 91 | 95 |
| 99 | 103 | 107 | 111 |
| 115 | 119 | 123 | 127 |
| 131 | 135 | 139 | 143 |
| 147 | 151 | 155 | 159 |
| 163 | 167 | 171 | 175 |
| 179 | 183 | 187 | 191 |
| 195 | 199 | 203 | 207 |
| 211 | 215 | 219 | 223 |
| 227 | 231 | 235 | 239 |
| 243 | 247 | 251 | 255 |
Here 16 even operations occur for 16 consecutive clock cycles followed by 16 odd operations, and so on. As mentioned earlier, the twiddle factors are shared between even and odd and will be repeated per even/odd chunk.
In the last round, the output of memory is as follows:
| 7&8 | |||
|---|---|---|---|
| 0 | 1 | 2 | 3 |
| 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
| 32 | 33 | 34 | 35 |
| 36 | 37 | 38 | 39 |
| 40 | 41 | 42 | 43 |
| 44 | 45 | 46 | 47 |
| 48 | 49 | 50 | 51 |
| 52 | 53 | 54 | 55 |
| 56 | 57 | 58 | 59 |
| 60 | 61 | 62 | 63 |
| 64 | 65 | 66 | 67 |
| 68 | 69 | 70 | 71 |
| 72 | 73 | 74 | 75 |
| 76 | 77 | 78 | 79 |
| 80 | 81 | 82 | 83 |
| 84 | 85 | 86 | 87 |
| 88 | 89 | 90 | 91 |
| 92 | 93 | 94 | 95 |
| 96 | 97 | 98 | 99 |
| 100 | 101 | 102 | 103 |
| 104 | 105 | 106 | 107 |
| 108 | 109 | 110 | 111 |
| 112 | 113 | 114 | 115 |
| 116 | 117 | 118 | 119 |
| 120 | 121 | 122 | 123 |
| 124 | 125 | 126 | 127 |
| 128 | 129 | 130 | 131 |
| 132 | 133 | 134 | 135 |
| 136 | 137 | 138 | 139 |
| 140 | 141 | 142 | 143 |
| 144 | 145 | 146 | 147 |
| 148 | 149 | 150 | 151 |
| 152 | 153 | 154 | 155 |
| 156 | 157 | 158 | 159 |
| 160 | 161 | 162 | 163 |
| 164 | 165 | 166 | 167 |
| 168 | 169 | 170 | 171 |
| 172 | 173 | 174 | 175 |
| 176 | 177 | 178 | 179 |
| 180 | 181 | 182 | 183 |
| 184 | 185 | 186 | 187 |
| 188 | 189 | 190 | 191 |
| 192 | 193 | 194 | 195 |
| 196 | 197 | 198 | 199 |
| 200 | 201 | 202 | 203 |
| 204 | 205 | 206 | 207 |
| 208 | 209 | 210 | 211 |
| 212 | 213 | 214 | 215 |
| 216 | 217 | 218 | 219 |
| 220 | 221 | 222 | 223 |
| 224 | 225 | 226 | 227 |
| 228 | 229 | 230 | 231 |
| 232 | 233 | 234 | 235 |
| 236 | 237 | 238 | 239 |
| 240 | 241 | 242 | 243 |
| 244 | 245 | 246 | 247 |
| 248 | 249 | 250 | 251 |
| 252 | 253 | 254 | 255 |
The challenge here is that even and odd coefficients are mixed which is not allowed in ML-KEM. In order to compute the last layer (layer 7) with least complexity and overhead, we propose a method to swap the inputs to the butterfly cores such that even and odd pairs of coefficients are computed separately. We propose an architecture where the second stage of butterfly units do not perform any operations during this last round of ML-KEM NTT which results in correct arithmetic computation.
To preserve correctness of memory layout, we propose a method to further swap the outputs of butterfly units such that they are regrouped to be in the same order as they were read from the memory. This allows subsequent NTT/INTT operations to have access to the correct memory contents without the need for a post-processing step thus reducing both complexity and latency, while maintaining high security.
Figure below shows the 2x2 architecture indicating data swapping and 7-layer computation in ML-KEM.

Passthrough control signal is asserted when performing the last stage 7 in ML-KEM NTT and will disable the second set of butterfly cores leading to a passthrough of inputs to outputs. Since the inputs of the second stage are swapped, the correct order of coefficients is written to memory.
- Memory access pattern for INTT
The following figure shows example memory contents for an INTT operation in Dilithium:
| Address | 1&2 | ||||
|---|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 3 | |
| 1 | 4 | 5 | 6 | 7 | |
| 2 | 8 | 9 | 10 | 11 | |
| 3 | 12 | 13 | 14 | 15 | |
| 4 | 16 | 17 | 18 | 19 | |
| 5 | 20 | 21 | 22 | 23 | |
| 6 | 24 | 25 | 26 | 27 | |
| 7 | 28 | 29 | 30 | 31 | |
| 8 | 32 | 33 | 34 | 35 | |
| 9 | 36 | 37 | 38 | 39 | |
| 10 | 40 | 41 | 42 | 43 | |
| 11 | 44 | 45 | 46 | 47 | |
| 12 | 48 | 49 | 50 | 51 | |
| 13 | 52 | 53 | 54 | 55 | |
| 14 | 56 | 57 | 58 | 59 | |
| 15 | 60 | 61 | 62 | 63 | |
| 16 | 64 | 65 | 66 | 67 | |
| 17 | 68 | 69 | 70 | 71 | |
| 18 | 72 | 73 | 74 | 75 | |
| 19 | 76 | 77 | 78 | 79 | |
| 20 | 80 | 81 | 82 | 83 | |
| 21 | 84 | 85 | 86 | 87 | |
| 22 | 88 | 89 | 90 | 91 | |
| 23 | 92 | 93 | 94 | 95 | |
| 24 | 96 | 97 | 98 | 99 | |
| 25 | 100 | 101 | 102 | 103 | |
| 26 | 104 | 105 | 106 | 107 | |
| 27 | 108 | 109 | 110 | 111 | |
| 28 | 112 | 113 | 114 | 115 | |
| 29 | 116 | 117 | 118 | 119 | |
| 30 | 120 | 121 | 122 | 123 | |
| 31 | 124 | 125 | 126 | 127 | |
| 32 | 128 | 129 | 130 | 131 | |
| 33 | 132 | 133 | 134 | 135 | |
| 34 | 136 | 137 | 138 | 139 | |
| 35 | 140 | 141 | 142 | 143 | |
| 36 | 144 | 145 | 146 | 147 | |
| 37 | 148 | 149 | 150 | 151 | |
| 38 | 152 | 153 | 154 | 155 | |
| 39 | 156 | 157 | 158 | 159 | |
| 40 | 160 | 161 | 162 | 163 | |
| 41 | 164 | 165 | 166 | 167 | |
| 42 | 168 | 169 | 170 | 171 | |
| 43 | 172 | 173 | 174 | 175 | |
| 44 | 176 | 177 | 178 | 179 | |
| 45 | 180 | 181 | 182 | 183 | |
| 46 | 184 | 185 | 186 | 187 | |
| 47 | 188 | 189 | 190 | 191 | |
| 48 | 192 | 193 | 194 | 195 | |
| 49 | 196 | 197 | 198 | 199 | |
| 50 | 200 | 201 | 202 | 203 | |
| 51 | 204 | 205 | 206 | 207 | |
| 52 | 208 | 209 | 210 | 211 | |
| 53 | 212 | 213 | 214 | 215 | |
| 54 | 216 | 217 | 218 | 219 | |
| 55 | 220 | 221 | 222 | 223 | |
| 56 | 224 | 225 | 226 | 227 | |
| 57 | 228 | 229 | 230 | 231 | |
| 58 | 232 | 233 | 234 | 235 | |
| 59 | 236 | 237 | 238 | 239 | |
| 60 | 240 | 241 | 242 | 243 | |
| 61 | 244 | 245 | 246 | 247 | |
| 62 | 248 | 249 | 250 | 251 | |
| 63 | 252 | 253 | 254 | 255 |
In INTT, the coefficients are read and executed in order, i.e., in cycle 0, the coefficients executed are {0, 1, 2, 3}, etc.
- INTT memory access resolution for ML-KEM
In ML-KEM, since even and odd coefficients need to be grouped together, the first stage of butterfly operations must be bypassed. However, since the first stage of butterfly units support masking countermeasure, the inputs are swapped in INTT mode and passed onto the first stage and the second butterfly stage is skipped, which results in correct arithmetic results in ML-KEM INTT operation. The following architecture indicates the passthrough mode for INTT and NTT modes of operation:

Remaining layers of INTT are similar to ML-KEM NTT where even and odd operations alternate for 16, 4 and 1 clock cycles and both butterfly stages are engaged. Twiddle factors are shared between even and odd operations and in the proposed method, the twiddle factors will be appropriately driven to maintain arithmetic correctness.
The butterfly operation is determined by a mode input that indicates whether current operation is NTT or INTT. The ntt_passthrough flag is asserted in NTT mode during the last round of operation. The intt_passthrough flag is asserted in INTT mode during the first round of operation. Once asserted, the second butterfly stage is skipped and the outputs of first stage are appropriately swapped and directly passed onto outputs of the 2x2 architecture.
Modular Reduction
Barrett reduction is an efficient method for computing z mod q without performing division. Instead, it precomputes an approximation of 1/q, allowing the reduction to be done with multiplications and shifts.
Given an integer z and a modulus q, Barrett reduction works as follows:
- Precompute the scaling factor ΞΌ=β2kqβ.
- Approximate the quotient βz/qβ as u=β(z)/2kβ
- Compute the remainder as r=z-uq
- Final correction step to see if rβ₯q then subtract q to ensure r is in the range [0,q-1].
For ML-KEM hardware accelerator, we can customize the reduction architecture based on the prime value of the scheme with q= 3,329 to design a hardware-friendly architecture that increases the efficiency of computation.
Suppose that all input operands are less than q, we have:
0β€a,b<q
z=a.b<q2=24'hA9_1A01
Since the input value for reduction is presented in 24 bits, k is set to 24. In that case, we have:
ΞΌ=β2kqβ=β2243329β=5039
The figure below illustrates the architecture of this reduction technique, which requires two multiplications, along with a shifter and subtractor.

The modular multiplication is implemented with a 2-stage pipeline architecture. At the first pipeline stage, z=aΒ·b is calculated. At the second pipeline stage, the reduction is executed to obtain the result and the result is output.
The operations of our reduction do not depend on the input data and do not leak any information. Our reduction using the modulus q= 3329 is fast, efficient and constant-time.
PWM
Polynomials are represented in the ring Rq=ZqX/XN+ 1, meaning they are defined over the field of integers modulo q with a degree constraint of N-1. A polynomial a(x) is expressed as a sum of monomials with integer coefficients:
ax= i=0N-1aiXi
Where aiβ are the coefficients of the polynomial, and each belongs to Zq.
NTT presentation of this polynomial is shown as a=NTT(a) such that
ai=j=0N-1ainij mod q
and n is n-th primitive root of unity such that nn=1 mod q.
The multiplication of two polynomials cx= axbx can be performed efficiently in NTT domain follows modular polynomial arithmetic under the constraint XN+1=0. The product polynomial c(x) is computed as:
cx= axbx mod XN+ 1
Expanding this multiplication:
cx=i=0N-1aibiXi
Where is point-wise multiplication in NTT domain.
In ML-KEM, the incomplete Number-Theoretic Transform (NTT) representation alters the conventional point-wise multiplication used in NTT-based polynomial multiplication. Unlike fully transformed polynomials, where coefficients are directly multiplied element-wise, ML-KEM requires a pair-wise multiplication scheme.
Specifically, in ML-KEMβs NTT domain, the multiplication must be performed between paired coefficients from two input polynomials. This means that an even-indexed coefficient a2iβ and its corresponding odd-indexed coefficient a2i+1β from the first polynomial must be combined with the even- and odd-indexed coefficients b2iβ and b2i+1 from the second polynomial according to the following formula:
c2i+c2i+1X=a2iΒ β Β b2i+a2i+1b2i+12bri+1+a2ib2i+1+a2i+1b2iXΒ Β 0β€i<128
Where
c2i=a2iΒ β Β b2i+a2i+1b2i+12bri+1
c2i+1=a2ib2i+1+a2i+1b2i
where represents the twiddle factor used for proper coefficient alignment in the transformation, and br(i) denotes the bit-reversal permutation applied to the index i.
Each pair-wise multiplication in ML-KEM's NTT domain involves five multiplication operations per index pair. Since multiplication is a computationally expensive operation in hardware, reducing the number of multiplications can significantly improve efficiency and reduce resource utilization.
To optimize the implementation, we apply Karatsubaβs algorithm to reduce the number of required multiplications by leveraging algebraic decomposition. Given two operands x and y, Karatsuba multiplication decomposes them into two smaller parts:
x=x12k+x0
y=y12k+y0
directly computing the product using four multiplications can be presented as follows:
xβ y=x12k+x0y12k+y0=x1y122k+(x1y0+x0y1)2k+x0y0
While Karatsuba reduces it to three:
xβ y=x12k+x0y12k+y0=x1y122k+(x1+x0y1+y0-x0y0-x1y1)2k+x0y0
This technique reduces the number of multiplications from four to three at the cost of additional additions and subtractions, which are significantly less expensive in hardware.
In ML-KEMβs pair-wise multiplication formula can be optimized using Karatsubaβs method. By applying the decomposition to these terms, we can effectively replace five multiplications with a reduced set of four per pair-wise operation, minimizing the hardware cost while maintaining computational correctness.
c2i+c2i+1X=a2iΒ β Β b2i+a2i+1b2i+12bri+1+a2ib2i+1+a2i+1b2iX=a2iΒ β Β b2i+a2i+1b2i+12bri+1+a2i+a2i+1b2i+b2i+1-a2iΒ β Β b2i-a2i+1b2i+1XΒ Β 0β€i<128
c2i=a2iΒ β Β b2i+a2i+1b2i+12bri+1
c2i+1=a2i+a2i+1b2i+b2i+1-a2iΒ β Β b2i-a2i+1b2i+1
This optimization significantly enhances the efficiency of the ML-KEM NTT implementation, making it more suitable for hardware acceleration while preserving the correctness of polynomial multiplication.
The hardware architecture of this proposal is shown below while all operations are modular:

The proposed Karatsuba-based optimization is well aligned with our memory architecture, which stores four polynomial coefficients per address. Our memory architecture is shown below:

This structure allows efficient loading and processing of coefficient pairs, reducing the number of memory accesses required during multiplication. Since Karatsubaβs method naturally decomposes polynomial multiplication into smaller subproblems involving coefficient pairs, it maps well onto our memory organization, enabling parallel processing of multiple coefficient pairs in a single memory fetch.

Rejection Sampler
Rejection sampling is integral to the KeyGen and Encapsulation processes of ML-KEM. It generates polynomials that are directly utilized in polynomial multiplications within the Number Theoretic Transform (NTT) domain. ML-KEM utilizes SHAKE-128, Keccak processes an input seed concatenated with nonce values (describing polynomial indices) to generate a pseudorandom bit stream. To optimize this process, our design focuses on Parallel-Input Serial-Output (PISO) Buffering, optimizing the rejection unit to perform efficient polynomial multiplication.
A PISO unit stores Keccak outputs, sequentially feeding data into the rejection sampler to ensure a steady input flow. The rejection sampler selectively retains 12-bit samples below the modulus q=3329, discarding values exceeding this threshold. The output is a 256-coefficient polynomial structured as:
A=A0,0 A0,k-1 β± Ak-1,0 Ak-1,k-1 kΓk
The value of k is determined based on the security level of the system defined by NIST as follows:
| Algorithm Name | Security Level | k |
|---|---|---|
| ML-KEM-512 | Level-1 | 2 |
| ML-KEM-768 | Level-3 | 3 |
| ML-KEM-1024 | Level-5 | 4 |
To enhance polynomial multiplication efficiency, the sampled polynomials undergo pointwise multiplication, reducing computational overhead by eliminating unnecessary memory accesses as follows:
A0,0 A0,k-1 β± Ak-1,0 Ak-1,k-1 Β°s1,0 s1,k-1 =A0,0Β Β°s1,0+β¦+A0,k-1Β°s1,k-1 Ak-1,0Β°s1,0+β¦+Ak-1,k-1Β°s1,k-1
We propose an architecture to remove the cost of memory access from Keccak to rejection sampler, and from rejection sampler to polynomial multiplier using a on-the-fly technique. To achieve this, we need to have a balanced throughput between all these modules to avoid large buffering or conflict between them.
High-level architecture is illustrated as follows:

Keccak is used in SHAKE-128 configuration for rejection sampling operation. Hence, it will take the input data and generates 1344-bit output after each round. We propose implementing of Keccak while each round takes 12 cycles. The format of input data is as follows:
Input data = Ο | j | i
Where is seed with 256-bits, i and j are nonce that describes the row and column number of corresponding polynomial A such that:
Ai,j=Rejection_sampling(Keccak(Ο | j | i))
Since each 12-bit is used for one coefficient, each round of Keccak output provides 1344/12=112 coefficients. There are two paths for Keccak input. While the input can be set by controller for each new polynomial, the loop path is used to rerun Keccak for completing the previous polynomial. Rejection sampler cannot take all 1344-bit output parallelly since it makes hardware architecture too costly and complex, and also there is no other input from Keccak for the next 12 cycles. Therefore, we propose a parallel-input serial-output (PISO) unit in between to store the Keccak output and feed rejection unit sequentially.
The rejection sampler takes data from the output of SHAKE-128 stored in a PISO buffer. The required cycles for this unit are variable due to the non-deterministic pattern of rejection sampling. In our optimized architecture, this unit works in parallel with the Keccak core. Therefore, the latency for rejection sampling is absorbed within the latency for a concurrently running Keccak core.
Our proposed polynomial multiplier can perform point-wise multiplication on four coefficients per cycle that also helps to avoid memory access challenges and make the control logic too complicated. This implies that the optimal speed of the rejection sampling module is to sample four coefficients without rejection in one cycle.
On the output side, as the rejection sampling might fail, the rejection rate for each input is:
rejection_rate= 1-q212=1-3329212=0.187
Hence, the probability of failure to provide 4 appropriate coefficients from 4 inputs would be:
1-1-rejectionrate4=1-1-0.1874=0.56
To reduce the failure probability and avoid any wait cycle in polynomial multiplication, we need more coefficients are fed into rejection while only 4 of them will be passed to polynomial multiplication. This decision reduces the probability of failure. The following equation computes failure probability of having at least 4 valid samples out of N inputs:
PXβ₯4=1-k=03PX=k
PX=k=N k pk1-pN-k
The failure probability for different N as shown below:
| Number of Inputs (N) | Failure Probability |
|---|---|
| 4 | 0.56 |
| 5 | 0.23 |
| 6 | 0.08 |
| 7 | 0.02 |
| 8 | 0.007 |
| 9 | 0.002 |
| 10 | 0.0005 |
To balance the hardware resource utilization while improving the performance, we chose to have 10 input samples. Adding a FIFO to rejection sampling unit can store the remaining unused coefficients and increase the probability of having 4 appropriate coefficients to match polynomial multiplication throughput. The architecture is as follows:

There are 10 rejection sampler circuits corresponding to each 12-bit input. The controller checks if each of these coefficients should be rejected or not. The valid input coefficients can be stored into the FIFO. While a maximum of 10 coefficients can be fed into FIFO, there are three more entries for the remaining coefficients from the previous cycle. There are several scenarios for the proposed balanced throughput architecture:
- At the very first cycle, or whenever the FIFO is empty, rejection sampling unit may not provide all 4 coefficients for the polynomial multiplication unit. We reduce the failure probability of this scenario by feeding 10 coefficients, however, it may happen. So, for designing efficient architecture, instead of reducing the failure probability by adding more hardware cost, we use a VALID output that stops the polynomial multiplier until all 4 required coefficients are sampled.
- If all 10 inputs are valid, they are going to be stored into FIFO. The first 4 coefficients will be sent to the polynomial multiplication unit, while the remaining coefficients will be shifted to the head of FIFO and be used for the next cycle.
- The FIFO has a maximum depth of 14 entries. When 9 or more entries are filled, a pop operation reduces the occupancy to 5, which is still insufficient to accommodate new inputs while allowing the rejection sampling unit to continue outputting data in upcoming cycles. As a result, the FIFO asserts the FULL flag to block further input from the Keccak core.
| Cycle count | Input from PISO | FIFO valid entries from previous cycle | Total valid samples | output | FIFO remaining for the next cycle |
|---|---|---|---|---|---|
| 0 | 10 | 0 | 10 | 4 | 6 (FULL) |
| 1 | 0 | 6 | 6 | 4 | 2 |
| 2 | 10 | 2 | 12 | 4 | 8 (FULL) |
| 3 | 0 | 8 | 8 | 4 | 4 |
| 4 | 10 | 4 | 14 | 4 | 10 (FULL) |
| 5 | 0 | 10 | 10 | 4 | 6 |
| 6 | 10 | 6 | 13 | 4 | 9 (FULL) |
- If there is not FULL condition for reading from Keccak, all PISO data can be read in 11 cycles. This would match with Keccak throughput that generates 112 coefficients per 12 cycles.
- The maximum number of FULL conditions is when there are no rejected coefficients for all 112 inputs. In this case, after every cycle with 10 coefficients, there is one FULL condition. It takes 28 cycles to process all PISO contents. To maximize the utilization factor of our hardware resources, Keccak core will check the PISO status. If PISO contains 8 coefficients or more (the required inputs for rejection sampling unit), EMPTY flag will not be set, and Keccak will wait until the next cycle. Hence, the Keccak output will be stored into PISO when all PISO contents are processed by rejection sampler.

CBD sampler
ML-KEM ML-KEM, a lattice-based key encapsulation mechanism (KEM), relies on binomial sampling to generate small polynomials with coefficients following a centered binomial distribution (CBD). This distribution is crucial for ensuring cryptographic security while maintaining efficiency in polynomial arithmetic. In ML-KEM, the binomial parameter Ξ· varies depending on the security level, where Ξ· = 3 for ML-KEM-512 and Ξ· = 2 for ML-KEM-768 and ML-KEM-1024.
ML-KEM ML-KEM relies on a specialized distribution, D(Rq), to generate polynomials with small coefficients, commonly referred to as "errors" or "noise". This distribution is parameterized by an integer Ξ·. To generate a polynomial from D(Rq), each coefficient is sampled independently from a centered binomial distribution (CBD) over . The sampling process follows the following equation, which produces polynomial coefficient arrays based on an input stream of uniformly random bytes. Let be the Keccak output, the coefficients are computed as follows:
ei=j=0j=Ξ·-12iΞ·+j-j=0j=Ξ·-12iΞ·+Ξ·+j
which turns uniformly distributed samples into binomial distribution.
To have a configurable Hamming weight computation, the CBD sampler extracts 8Ξ· random bits per coefficient from a pseudorandom bit stream. Then it computes the Hamming weight difference between the first and second halves of these bits to derive the binomially distributed coefficient. A control unit dynamically adjusts the sampling logic based on the selected Ξ· value, allowing seamless reconfiguration.

Our proposed data flow ensures that 4 coefficients are computed per cycle, significantly improving throughput compared to conventional serial samplers. A parallel-input serial-output (PISO) buffer is employed to manage input bitstreams efficiently, ensuring a steady supply of random bits.

The binomial sampler operates concurrently with Keccak, reducing overall latency. By leveraging a pipelined structure, the design ensures that binomial sampling does not become a performance bottleneck for polynomial arithmetic operations. A PISO buffer stores intermediate values, reducing redundant computations and improving resource utilization. The architecture supports dynamic switching between Ξ· values based on control signals, making it adaptable for different security levels.
For Ξ· = 2, each polynomial requires 1024 bits. Since SHAKE256 provides 1088 bits per round, a single Keccak round suffices. However, for Ξ· = 3, each polynomial requires 1536 bits (256 Γ 3 Γ 2 = 1536), necessitating two Keccak rounds to generate enough entropy.
The configurable design reduces hardware redundancy by allowing a single sampling unit to support multiple security levels. Generating 4 coefficients per cycle improves overall efficiency, aligning the samplerβs throughput with memory architecture, polynomial multiplication and other arithmetic operations. Parallelism and buffering mechanisms prevent stalls, ensuring that sampling latency does not impact system performance.
Compress/Decompress
The compression stage in ML-KEM takes full 12-bit polynomial coefficients and reduces them to a smaller representation of d bits, where d varies depending on the compression level. This design explicitly supports d values of 1, 5, 11, and 12. The value d = 12 is used to implement the byte encode and byte decode functions of the ML-KEM algorithm. This compression is a lossy operation that approximates a division by q = 3329, mapping each coefficient in Zq to a smaller domain suitable for compact ciphertext encoding. In hardware, this typically requires multiplication by a scaling factor, followed by division and rounding operations that are expensive in terms of logic and latency.

To optimize this process, we propose a hardware-efficient compression architecture that processes four 12-bit coefficients per clock cycle. Each coefficient undergoes configurable shifts and additions to approximate the division operation.
To accurately implement the compression formula in hardware, we adopt the following formula:
((data[11:0] << d) + q/2) / q
where data is a 12-bit coefficient and d is the target compressed bit-width. The term q2β is added prior to division to address the rounding behavior of hardware right shifts. In hardware implementations, division is implemented using right shifts, which inherently perform truncation (i.e., rounding down). This creates a systematic bias in the compressed output. By adding q2β before the division, we effectively round the result to the nearest integer rather than always rounding down. This adjustment is critical for maintaining the fidelity of the compression process and ensures that the mapping from Zqβ to the reduced d-bit representation is as accurate as possible, without introducing additional complexity to the hardware. The approach is both efficient and hardware-friendly, requiring only a simple addition and no costly division circuitry.
The following figure shows the compression map where d=1:

Rather than implementing a general-purpose division unit, which would significantly increase area, we leverage a dual-purpose Barrett reduction module, originally included for use in the Number-Theoretic Transform (NTT) and inverse NTT stages. This is a key novelty of our design: by reusing the Barrett logic for both modular reduction and compression scaling, we eliminate the need for redundant arithmetic units and improve overall resource utilization.
Barrett reduction is a well-established technique used in lattice-based cryptographic schemes to reduce coefficients modulo a fixed constant, such as q = 3329, in a way that avoids full-width division. With careful parameter selection, the Barrett algorithm can be optimized to require only a single level of conditional subtraction, making it highly suitable for high-speed and low-area hardware implementations.

In our architecture, the same logic is reused for the division required during compression, allowing efficient rounding without the overhead of full-precision arithmetic.

Following compression, decompression performs the inverse operation by expanding d-bit coefficients back to 12-bit values in Zq βbased on the following formula.

Our architecture handles four such compressed coefficients per clock cycle using four parallel multipliers. Each compressed coefficient is scaled and shifted based on its original encoding formula to recover the approximate 12-bit representation. The decompression path supports multiple d values via a configurable datapath, enabling seamless integration into systems supporting different compression profiles for ML-KEM using the following equation.
((data[d-1:0] * q) + (2^(d-1))) >> d
The addition of Β 2d-1 serves the same rounding purpose as q2β in the compression path. Since right shifts truncate in hardware, adding Β 2d-1 ensures the result is rounded to the nearest integer instead of being consistently biased downward. As with compression, the technique is hardware-friendly and avoids complex division or floating-point logic, while delivering improved accuracy through a simple constant addition and shift. The proposed architecture for decompression is shown as follows:

To maximize efficiency, the decompression and compression stages share arithmetic resources wherever possible. Multipliers, shift-add networks, and routing logic are multiplexed between the two operations, enabling compact hardware without sacrificing throughput. The shared Barrett reduction module is a central feature of this architecture, serving both traditional modular reduction in the NTT/INTT flow and the scaled division in compression.
This approach not only improves area efficiency but also simplifies integration and control logic. By avoiding dedicated division circuits and leveraging existing reduction infrastructure, our architecture delivers high-performance, low-area compression and decompression tailored for the demands of post-quantum cryptography.
High-Level architecture
In our proposed architecture, we define specific instructions for various submodules, including SHAKE256, SHAKE128, SHA3-512, SHA3-256, NTT, INTT, etc. Each instruction is associated with an opcode and operands. By customizing these instructions, we can tailor the engine's behavior to different security levels.
To execute the required instructions, a high-level controller acts as a sequencer, orchestrating a precise sequence of operations. Within the architecture, several memory blocks are accessible to submodules. However, it's the sequencer's responsibility to provide the necessary memory addresses for each operation. Additionally, the sequencer handles instruction fetching, decoding, operand retrieval, and overall data flow management.
The high-level architecture of Adams Bridge controller is illustrated as follows:

Sequencer
High-Level controller works as a sequencer to perform a specific sequence of operations. There are several blocks of memory in the architecture that can be accessed by sub-modules. However, the sequencer would be responsible for passing the required addresses for each operation.
As an example, an NTT operation needs to take three base addresses as follows:
NTT(initialvalue_base_address, intermediatevalue_base_address, result_base_address)
So, for performing a=NTT(b), the sequencer needs to be:
NTT(a_base_address, temp_base_address, b_base_address)
The following table lists different operations used in the high-level controller.
Keccak and samplers Opcodes:
| Instruction | Description |
|---|---|
| RST_Keccak | Reset the Keccak SIPO buffer |
| EN_Keccak | Enable the Keccak |
| LDKeccak_MEM src, len | Load Keccak SIPO buffer at memory address src in len -width |
| LDKeccak_REG src, len | Load Keccak SIPO buffer at register ID src in len -width |
| RDKeccak_MEM dest, len | Read Keccak PISO buffer and store it at memory address dest in len -width |
| RDKeccak_REG dest, len | Read Keccak PISO buffer and store it at register ID dest in len -width |
| REJ_SMPL | Start Keccak and rejection sampler (results is used by PWM) |
| CBD_SMPL | Start Keccak and CBD sampler |
NTT/INTT/PWO Opcodes:
| Instruction | Description |
|---|---|
| NTT src, temp, dest | Perform NTT on data at memory address src and store the results at address dest |
| INTT src, temp, dest | Perform INTT on data at memory address src and store the results at address dest |
| PWM src0, src1, dest | Perform PWM on data at memory address src0 and src1 and store the results at address dest (dest = src0*src1) |
| PWM_SMPL src, dest | Perform PWM on data from sampler and at memory address src and store the results at address dest (dest = smpl*src) |
| PWM_ACCU src0, src1, dest | Perform PWM in accumulation mode on data at memory address src0 and src1 and store the results at address dest (dest = src0*src1+dest) |
| PWM_ACCU_SMPL src, dest | Perform PWM in accumulation mode on data from sampler and at memory address src and store the results at address dest (dest = smpl*src + dest) |
| PWA src0, src1, dest | Perform PWA on data at memory address src0 and src1 and store the results at address dest (dest = src0+src1) |
| PWS src0, src1, dest | Perform PWS on data at memory src0 and src1 and store the results at address dest (dest = src0-src1) |
Other Opcodes:
| Instruction | Description |
|---|---|
| COMPRESS src, mode, dest | Perform compress on data at memory address src with mode configuration and store the results at address dest |
| DECOMPRESS src, mode, dest | Perform decompress on data at memory address src with mode configuration and store the results at address dest |
References:
[1] NIST, "FIPS 203 Module-Lattice-Based Key-Encapsulation Mechanism Standard," August 13, 2024.
Core Registers
Redirecting to external documentation...
Click here if not redirected β
2041523
Caliptra Subsystem Hardware Specification
Version 2p1
- Scope
- Caliptra Subsystem Requirements
- Caliptra Subsystem Architectural Flows
- I3C Streaming Boot (Recovery) Interface
- AXI Streaming Boot (Recovery) Interface
- Caliptra Core AXI Manager & DMA assist
- Caliptra Subsystem Fuse Controller
- Caliptra Subsystem Life Cycle Controller
- Caliptra Subsystem, SOC Debug Architecture Interaction
- Caliptra Subsystem LCC State Definitions
- DFT & DFD Life-cycle States
- LCC State and State Decoder Output Ports
- TAP Pin Muxing
- Production Debug Unlock Architecture
- Masking Logic for Debugging Features in Production Debug Mode (MCI)
- LCC Interpretation for Caliptra βCoreβ Security States
- Exception: Non-Volatile Debugging Infrastructure and Initial RAW State Operations
- LCC RAW Unlock Token Generation & Setting
- SOC LCC Interface usage & requirements
- LCC Module: Summarized Theory of operation
- Manufacturer Control Unit (MCU)
- Manufacturer Control Interface (MCI)
- Overview
- MCI Feature Descriptions
- MCI Debug
- MCI Design for Test (DFT)
Scope
This document defines technical specifications for a subsystem design utilizing Caliptra RoT in Subystem Mode. This document, along with Caliptra Hardware Specification, shall comprise the Caliptra Subsystem technical specification.
Document Version
| Date | Document Version | Description |
|---|---|---|
| Jan 31st, 2025 | v0p8 | Work in progress |
| Apr 30th, 2025 | v1p0-rc1 | Initial release candidate of Caliptra Gen 2.0 Subsystem Documents. Specifcations updated with: - Updated details on component features (MCU mailbox, MCU trace buffer, MCI error aggregation, FC fuse zeroization) - Details on design connectivity with top-level ports - Port and register updates |
| Oct 12th, 2025 | v2p1 | Final release of Caliptra Subsystem 2.1 |
Caliptra Subsystem Requirements
Definitions
- RA: Recovery Agent / Streaming boot Agent
- MCI: Manufacturer Control Interface
- MCU: Manufacturer Control Unit
Caliptra-Passive-Mode (Legacy)
SOC manages boot media peripherals and Caliptra is used as Root of trust for measurements.
Caliptra-subsystem-mode
Caliptra owns the recovery interface (peripheral independent) and Caliptra is THE RoT of the SoC. Any SOC specific variables that needs to be stored and retrieved securely from NV storage is handled using Caliptra.
Caliptra Subsystem Architectural Requirements
- MCU [Manufacturer Control Unit] runs Manufacturer specific FW authenticated, measured & loaded by Caliptra at boot time.
- MCU will use Caliptra RT FW to auth/measure/load all of the FW belonging to the SOC.
- MCU ROM/FW on MCU should be capable of performing SOC specific initialization.
- MCU ROM/FW should be able to perform SoC management functions including performing reset control, SoC specific security policy enforcement, SOC initial configuration (eg. any GPIO programming, glitch detection circuits, reading/moving non-secret fuses etc.).
- Note: Widgets that toggle the reset or other wires that set security permissions are SOC specific implementations.
- Fuse controller for provisioning Caliptra fuses -> IFP (In-field programmable) fusing is performed by MCU RT; SW partition fuses in fuse controller are managed by MCU (ROM or RT); Caliptra HW is responsible for reading the secret fuses (Caliptra ROM, MCU ROM or any other SOC ROM or any RT FW should NOT have access to read the secret fuses in production).
- Recovery stack must be implemented. Please refer to I3C recovery section for more details and references. OCP Recovery registers implemented in I3C must follow the security filtering requirements specified in the recovery implementation spec (eg. MCU can ONLY access subset of the recovery registers as defined by the recovery implementation).
- Supports silicon t0 boot to load and run required FW across chiplets.
- OCP recovery stack is implemented in Caliptra for Caliptra-subsystem-mode
- MCU SRAM (or part of the SRAM that is mapped for Code/Data execution) should be readable/writeable ONLY by Caliptra until Caliptra gives permission to MCU to use it.
- Caliptra can only load its FW, SOC manifest and MCU FW by reading the recovery interface registers.
- Caliptra ROM should know the offset of the recovery interface at its boot time. SOC has a choice to program this interface using MCU ROM or Hard strapped register address at integration time (Hard strapping at SOC integration time is recommended)
- Caliptra HW must know the offset of the registers where secrets (UDS, FE) and assets (PK hashes) exist at boot time. These should be hard strapped/configured at integration time.
- Caliptra ROM must know the address to where UDS-seed needs to be written to; this address should be hard strapped/configured at integration time.
- Any registers holding secrets/assets in fuse controller must follow the same rules as Caliptra 1.0/2.0 fuse register requirements (eg. clear on debug, clear on read once etc.)
- MCU SRAM and ROM sizes should be configurable at SOC integration time.
Caliptra Subsystem HW Requirements
Caliptra 2.0 HW requirements (Subsystem Support)
- Full AXI read/write channels (aka AXI manager) for subsystem (for MCU and Caliptra) a. For backward compatibility, AXI mgr. interface can be a no-connect and that configuration is validated.
- HW logic/Programmable DMA
- Read MMIO space for variable length.
- Data returned from the above read can be exposed directly to the FW OR allow it to be written to a subsystem/SOC destination as programmed by ROM/FW.
- Programmable logic to allow for SOC directed writes (from FW or from the above route back) to be sent through the SHA accelerator.
- (Future open/stretch goal): If AES engine accelerator is integrated into Caliptra, then implement decryption before sending the writes back to the destination programmed by the ROM/FW.
- This widget should have accessibility in manufacturing and debug mode over JTAG (can be exposed using the same JTAG interface as Caliptra 1.0). Ensure through validation that no asset can be read using this widget in those modes.
- Expand manuf flow register to include UDS programming request steps
- SOC Key Release HW (Required for OCP Lock flow too)
- Separate SOC Key Vault must be implemented (it is a separate structure from the current Caliptra KV).
- In at least one configuration, the SOC KV must be implemented as an SRAM that is external and configurable by the SOC OR or an internal configurable SOC KV structure. If this is achievable within the Caliptra 2.0 milestone, only one of these would be the chosen implementation and configuration. This will be a design decision based on effort & schedule.
- If implemented as a SRAM, data written and read into the SOC KV SRAM is decrypted & encrypted so that SOC DFT or other side channels cannot exfilterate the data.
- Caliptra FW will indicate to the SOC KV Release HW to release the key by supplying the key handle to read from.
- Destination address to which the key must be written to is programmed by the Caliptra FW into AXI MGR DMA HW.
- SOC KV must have the same attributes as Caliptra internal KV. Additionally, it must also have an attribute of read-once and clear.
Caliptra 2.0 HW requirements (Not subsystem related)
- Ability to use two or more cryptos concurrently
- Change APB -> AXI-Sub with the same capabilities (AXI USERID filtering replaces PAUSER based filtering, multiple targets for SHA acc, mailbox, fuses, registers etc. all)
- Future/Stretch Goal: Parity support on AXI-SUB & MGR
MCU HW requirements
- MCU should not be in the FIPS boundary and must not have any crypto functions. MCU must use Caliptra for any security flows (eg. security policy authorization) or traditional RoT support (eg. SPDM).
- MCU VeeR must support PMP & User mode.
- MCU AXI is directly connected to the SOC fabric in a way that allows a MMIO based control of the SoC as required for SOC security, manageability and boot functionality.
- MCU HW should add AXI-ID without any involvement of the ROM or FW running on the MCU; Implying any AXI access out of MCU (LSU side) should carry MCU USERID that HW populates.
- MCU executes from a SRAM that is at subsystem level.
- MCU uses instruction cache for speed up
- It is required that all NV read/writes (eg. NV variables in flash) that require a RoT support to securely store/restore must go through Caliptra.
- NV-storage peripheral shall be built in such a way that it will only accept transactions from MCU.
- Support for MCU first fetch vector to direct towards MCU SRAM post reset
Subsystem Components HW requirements
Fabric
- AXI Interconnect connects Caliptra, I3C, Fuse Controller, Life Cycle Controller, MCU and its memory components with the rest of the SOC.
- Note: Because each SOC integration model is different, AXI interconnect is NOT implemented by the subsystem but subsystem must validate using an AXI interconnect VIP to ensure all the components operate per the flows documented in this specification.
- For the VIP interconnect configuration, all subtractively decoded transactions are sent towards SoC. AXI interconnect & Subsystem is validated with the assumption that all of them are running on the same clock domain.
- To be documented: AXI-USERID requirements
MCU SRAM
- Since it's used for instruction and data execution β therefore requires AXI Sub with USERID filtering.
- Provide JTAG accessibility to allow for SRAM to be populated in a secured debug mode at power on time (debug policies will be same as Caliptra)
- MCU SRAM should have an aperture that can only be unlocked by Caliptra after it loads the image
- MCU SRAM has an aperture for MCU ROM to use part of the SRAM as a stack for its execution.
- MCU SRAM supports an aperture to be used as a MCU Mailbox.
MCU ROM
- MCU ROM also needs to have AXI Sub for any data access from MCU and thereby requires AXI-ID/USERID filtering.
I3C
- I3C on AXI interconnect with AXI Subordinate
- Spec 1.1.1 aligned, but only with optional features that are required per PCIe ECN # <>
- AXI Sub must be supported.
- UserID to MCU and Caliptra
- MCU access lock for I3C recovery and data (FIFO) registers until recovery flow is completed. In other words, MCU ROM must not impact the data flow into Recovery IFC registers. Stretch Goal: DMA data payload back to destination (Caliptra or MCU)
Fuse Controller
- AXI sub interface
- Secrets (separate USERID and access constraints) vs SW partition separation
- Registers implemented for βSecretsβ should follow the same rules as Caliptra (no scan, clear on specific life cycle/security states)
- Life cycle transition shall be implemented in hardware with no ROM/FW implementation and provides redundancy against fault & glitch attacks in the digital logic itself.
- SOC debug in production mode shall be supported with the help Caliptra through post quantum resilient algorithms.
- When debug mode is intended to be enabled & when enabled, all secrets/assets as defined should be wiped and provide the indication to SOC for any secrets/assets it may have.
MCI
- HW logic to move secret fuses from Fuse controller to Caliptra.
Caliptra Subsystem Hardware Block Diagram
Figure: Caliptra Subsystem Block Diagram

Caliptra Subsystem Architectural Flows
Please refer to Caliptra Security Subsystem Specification for more details.
I3C Streaming Boot (Recovery) Interface
The I3C recovery interface acts as a standalone I3C target device for recovery. It will have a unique address compared to any other I3C endpoint for the device. It will comply with I3C Basic v1.1.1 specification. It will support I3C read and write transfer operations. It must support Max read and write data transfer of 1-256B excluding the command code (1 Byte), length (2 Byte), and PEC (1 Byte), total 4 Byte I3C header. Therefore, max recovery data per transfer will be limited to 256-byte data.
I3C recovery interface is responsible for the following list of actions:
- Responding to command sent by Recovery Agent (RA)
- Updating status registers based on interaction of AC-RoT and other devices
- Asserting / Deasserting βpayload_availableβ & βimage_activatedβ signals
Streaming Boot (Recovery) Interface hard coded logic
Hardware registers size is fixed to multiple of 4 bytes, so that firmware can read or write with word boundary. Address offset will be programmed outside of the I3C device. Register access size must be restricted to individual register space and burst access with higher size must not be allowed.
Figure: I3C Streaming Boot (Recovery) Interface Logic Block Diagram

Hardware Registers
Hardware registers size is fixed to multiple of 4 bytes, so that firmware can read or write with word boundary. Address offset will be programmed outside of the I3C device. Register access size must be restricted to individual register space and burst access with higher size must not be allowed.
Note: Accessing the same address for INDIRECT_FIFO_DATA register will write or read the FIFO. It will not be available to access randomly as specified by the specification.
TODO: Add a link to rdl -> html file
Streaming Boot (Recovery) Interface Wires
- Payload available
- The Recovery Interface (I3C target) should receive a write transaction to INDIRECT_FIFO_DATA reg from BMC - 256B + 4B (Header), and wait for each I3C write to finish. Once I3C write transaction to INDIRECT_FIFO_DATA register is completed and PEC verification is successful, then the I3C target must assert "payload_available". DMA assist must wait for "payload_available" before reading. It must read 256B or last read with remaining data.
- The "payload_available" signal remains asserted until Recovery Interface receives Read from DMA over AXI for INDIRECT_FIFO_DATA.
- Image_activated
- The I3C target will assert "image_activated" signal as soon as write to RECOVERY_CTRL register is received.
- ROM will clear βimage_activatedβ bit by writing to RECOVERY_CTRL register via DMA assist after the image is authenticated. As defined in the OCP Recovery Specification, RECOVERY_CTRL, byte 2 is used to specify the image activation control, and is Write-1-Clear. ROM must write 0xFF to this field to clear the image recovery status, which will also result in the Recovery Interface deasserting the βimage_activatedβ signal to Caliptra.
I3C Streaming Boot (Recovery) Flow
Please refer to Caliptra Subsystem OCP Streaming Boot Sequence.
Figure: Caliptra Subsystem I3C Streaming Boot (Recovery) Flow

Caliptra ROM Requirements for OCP Streaming Boot
Caliptra firmware must follow these rules when implementing the OCP Streaming Boot flow:
- Caliptra ROM and RT Firmware must wait for "image_activated" signal to assert before processing the image.
- When image is sent to Caliptra Subsystem, Caliptra ROM & RT firmware must program DMA assist according to the rules defined in OCP Streaming Boot Payloads.
- Once the image is processed, Caliptra ROM & RT firmware must initiate a write with data 1 via DMA to clear byte 2 βImage_activatedβ of the RECOVERY_CTRL register. This will allow BMC (or streaming boot initiator) to initiate subsequent image writes.
I3C and Caliptra-AXI Interactions
Received transfer data can be obtained by the driver via a read from XFER_DATA_PORT register. Received data threshold is indicated to BMC by the controller with TX_THLD_STAT interrupt if RX_THLD_STAT_EN is set. The RX threshold can be set via RX_BUF_THLD. In case of a read when no RX data is available, the controller shall raise an error on the frontend bus interface (AHB / AXI).
AXI Streaming Boot (Recovery) Interface
This feature allows streaming data or firmware by MCU over the AXI bus of the I3C module which is repurposed as a streaming boot interface while disabling I3C usage.
AXI Streaming Boot Flow implementation
The AXI Streaming Boot flow reuses the logic already present in the I3C core used in the Caliptra-SS design, with a runtime option essentially bypassing most of the I3C core communication logic (including the I3C recovery flow logic).
The loopback functionality is configurable via the REC_INTF_CFG CSR which is set to I3C mode by default.
Streaming boot CSRs are accessible over AXI.
The transactions to the I3C core may be filtered using the AXI ID field.
The logic is implemented so that the streaming boot implementation in the Caliptra core (HW & ROM) can operate without any changes.
In order to enable setting W1C streaming boot registers, AXI streaming mode introduces an additional register - REC_INTF_REG_W1C_ACCESS.
AXI Streaming Boot Handler
In the regular (I3C) mode of the core, the Streaming Boot (Recovery) Handler strongly relies on the communication with the I3C Core internal logic by interfacing with TTI Queues. The bypass implementation modifies the I3C Core logic to allow direct access over the AXI bus to the structures specified by the OCP Streaming boot for compliance with the Caliptra Subsystem Streaming Boot Sequence.
The default design of the Streaming Boot Handler includes many blocks specifically designed to translate I3C bus traffic into recovery messages. It also automatically responds to the I3C commands by writing transaction descriptors and data for the TTI Queues.
Figure: Streaming Boot (Recovery) Handler in the I3C Core

In order enable the AXI streaming boot mechanism while reusing the existing logic and keeping compliance with Caliptra, the I3C core provides a custom bypass feature allowing direct communication with the Streaming Boot Handler via the AXI bus. The bypass disables the I3C communication logic. Data is routed from the TTI TX Queue to the Recovery Executor block, and written directly to the Indirect Data FIFO. The Caliptra ROM can access the data from the Indirect FIFO over the AXI bus (the same way it does in the regular I3C streaming boot flow). The dataflow in bypass mode (marked with green arrows) is depicted in the diagram below.
Figure: AXI Streaming Boot Handler in I3C Bypass Mode

AXI Streaming Boot CSRs
With the bypass feature enabled, the FIFO status CSRs in the Streaming Boot CSR file will be updated by the Streaming Bott Handler module.
However, some registers like e.g. INDIRECT_FIFO_CTRL which are updated by I3C commands in a standard streaming boot flow, will have to be accessed and configured properly from the software running on the Caliptra MCU via the AXI bus.
All configurable registers are writable from software, read only registers provide status information about Streaming Boot Handler internals, e.g. details about size and fill level of the Indirect FIFO.
Caliptra Core AXI Manager & DMA assist
SOC_IFC includes a hardware-assist block that is capable of initiating DMA transfers to the attached SoC AXI interconnect. The DMA transfers include several modes of operation, including raw transfer between SoC (AXI) addresses, moving data into or out of the SOC_IFC mailbox, directly encrypting/decrypting images with the AES block, and directly driving data through AHB CSR accesses to datain/dataout registers. One additional operating mode allows the DMA engine to autonomously wait for data availability via the OCP Recovery interface (which will be slowly populated via an I3C or similar interface).
Caliptra arms transfers by populating a transaction descriptor to the AXI DMA CSR block via register writes over the internal AHB bus. Once armed, the DMA will autonomously issue AXI transactions to the SoC until the requested data movement is completed. The DMA uses an internal FIFO to buffer transfer data. For any transfer using AXI Writes (Write Route is not disabled), the DMA waits until sufficient data is available in the FIFO before initiating any AXI write requests.
When arming the engine, Caliptra firmware is expected to have information about available AXI addresses that are legal for DMA usage. The DMA can facilitate transfer requests with 64-bit addresses. DMA AXI Manager logic automatically breaks larger transfer sizes into multiple valid AXI burst transfers (max 4KiB size), and legally traverses 4KiB address boundaries. The DMA AXI Manager supports a maximum transfer size of 1MiB in total. FIXED AXI bursts are allowed, to facilitate a FIFO-like operation on either read or write channels.

AXI Feature Support
The DMA assist initiates transactions on the AXI manager interface in compliance with the AXI specification. The generated AXI transactions adhere to the following rules:
- All address requests will be aligned to the data width of the interface, which is configured as 32-bit in the 2.0 release.
- The DMA will not issue Narrow transfers. That is, AxSIZE will always be set to match the data width of the interface (4 bytes).
- All data lanes are used on all transfers. That is, the AXI manager Write channel will always set WSTRB to all 1s.
- At most, 2 reads and 2 writes will be initiated at any time.
- When using a non-zero block_size for the transfer, in accordance with the Streaming Boot feature, at most 1 read and 1 write will be issued concurrently.
- All transactions are initiated with AxID = 0, meaning that both reads and writes require in-order responses.
- The maximum burst length initiated is constrained by the following parameters:
- Any transfer using the INCR burst type is restricted by both the maximum allowable length of an AXI transaction (AxLEN=255 or total byte count of 4KiB, whichever is smaller) and the size of the internal FIFO. In the 2.0 release the internal FIFO is configured with a depth of 512 bytes; the maximum transaction size allowed is 256 bytes, to allow up to 2 transactions to be outstanding at a time. In summary, the transfer size will always be 256 bytes or less (AxLEN = 63), as this is the smallest of (AxLEN = 255 -> 1KiB, 4KiB, and 256 bytes).
- Any transfer using the FIXED burst type is restricted by the AXI specification to a burst length of 16.
- When the block_size field is set to a non-zero value when arming the DMA, all transfers are restricted in size to specified block_size, in bytes.
- AXI transactions must not cross a 4KiB boundary, per the specification. If the transfer size requires crossing a boundary, the DMA will break it into smaller transactions that will go up to the boundary, then start from the next alignment boundary.
Routes
In the AXI DMA control state machine, both the read and write interfaces have their own βrouteβ. A route is configured for read and write channels for every DMA operation that is requested. Routes determine how data flows through the DMA block.
Read routes always apply when an AXI read is requested and must be disabled otherwise. Any AXI read pushes the response data into the internal FIFO. The read route determines where this data is ultimately sent when it is popped from the FIFO. For the AHB route, data from the FIFO is populated to the dataout register, where it may be read via AHB by the Caliptra RV core. For the mailbox route, data from the FIFO flows into the mailbox; for these operations, the mailbox address is determined by the destination address register value, with an offset applied based on how many bytes have been written to the mailbox. For the AXI route, data from the FIFO flows into the AXI Write Manager, whereupon it is sent out to AXI on the WDATA signal. For the Read DISABLE route, data output from the FIFO always flows into the AXI write channel. In this case, data originates from a source other than an AXI read, so AXI reads are inactive.
Write routes always apply when an AXI write is requested and must be disabled otherwise. Any AXI write reads data from the internal FIFO before sending it over AXI. The write route determines where this data originates before it is pushed onto the FIFO. For the AHB route, a write to the datain register via AHB results in write data being pushed onto the FIFO. For the mailbox route, data is read from the mailbox and pushed onto the FIFO; for these operations, the mailbox address is determined by the source address register value, with an offset applied based on how many bytes have been read from the mailbox. For the AXI route, data flows from the AXI Read Manager into the FIFO. For the Write DISABLE route, data received on the AXI read channel is always pushed into the FIFO and then sent to an internal destination rather than a destination on the AXI bus, so AXI writes are inactive.
Example 1: Write Route == MBOX and Read Route == DISABLE

Example 2: Read Route == AHB and Write Route == DISABLE

Example 3: Read Route == AXI and Write Route == AXI

OCP Streaming Boot Payloads
The DMA block supports a special mode of operation that is intended for use in reading Firmware Image Payloads (for Streaming Boot) from the Recovery Interface, present in the Caliptra Subsystem. This operation mode relies on three key control signals: the payload_available signal input from the recovery interface, the read_fixed control bit, and the block_size register (from the DMA control CSR block).
This special mode of operation is only used for AXI Reads. To trigger the DMA to operate in this mode, Caliptra Firmware must program the block_size register to a non-zero value that is a power-of-two. Caliptra Firmware must also program the read_fixed control bit. Once the βgoβ bit is set, causing the DMA to begin operation, the DMA control logic will wait for the payload_available input to trigger. When this occurs, the DMA controller issues a read that has a size equal to (or smaller) than the configured block_size (the read may be smaller if required by AXI rules). This process repeats until the amount of data indicated in the byte_size register has been transferred.
When programming an AXI to AXI transfer for a Streaming Boot Payload (e.g., to transfer an SoC Firmware manifest into an AXI-attached SRAM), firmware must also follow these rules:
- Programmed value of block_size must not exceed the largest legal AXI transaction size for a FIXED burst type. The DMA assist has a native data width of 4-bytes, so transaction size is restricted to 64-bytes due to the AXI specification limit of AxLEN == 15 for FIXED transactions.
- Destination address must be aligned to the programmed value of block_size. For example, if block_size is set to 64-bytes, destination address must be an integer multiple of 0x40.
In all cases other than reading Recovery Interface Payloads, the block_size register must be programmed to 0.
AES Mode
The DMA block can directly stream images from AXI to AES for decrypt/encrypt, and then stream the processed image over AXI to some SOC storage. This can be done by configuring the AXI DMA as:
- AES Mode == 1
- (optional) aes_gcm_mode in DMA == 1
- Read Route == AXI
- Write Route == AXI
- Block Size == 0 (OCP Stream Boot Disabled)
- Read/Write Fixed == 0
- Source Address == Start of image
- Destination Address == Start address where processed image should be stored
- Configure Byte Count - must be DWORD aligned
Other Read/Write Routes and Block sizes are not supported.
The AXI DMA AES Mode ONLY streams the image into and out of AES. It is Firmware's responsibility to:
- Fully configure AES before streaming the image via AXI DMA
- Push the IV, AAD header, and any other data into AES before streaming the image via AXI DMA
- Retrieve any tags from AES after the image has been processed.
If the input image size is not a multiple of 4 DWORDS, the AES FSM will properly pad the AES input data with 0s. When streaming the image out it reads all 4 DWORDS from the AES, but only writes up to the last DWORD of the image to the destination. If aes_gcm_mode register is set, at the start of the transfer it updates the GCM shadow byte count and phase registers in AES to the appropriate byte count and GCM_TEXT phase. Before the last block transfer to the AES it will again update the GCM shadow register to the appropriate values.
Input images can be in little endian or big endian. It is FW's responsibility to configure the AES wrapper's ENDIAN_SWAP register.
If AXI DMA encounters any errors (AXI or other errors) it will finish the transfer and report an error. It is the Firmware's responsibility to clear the error in the AXI DMA and flush the AES if required.
When AXI DMA is using AES Caliptra shall not try to access AES via AHB until AXI DMA has completed.
AES is not directly exposed on the AXI bus for external entities, it is protected behind the AXI DMA and can only be accessed via Caliptra uC AHB or the Caliptra DMA.
AES Mode: Read Route == AXI, Write Route == AXI, AES Mode == 1

Programming Flowchart
General Rules:
- If either Read or Write route is configured to AXI RD -> AXI WR, both routes must be configured as AXI RD -> AXI WR.
- Read Route and Write Route must not both be disabled.
- If Read Route is enabled to any configuration other than AXI RD-> AXI WR, Write route must be disabled.
- If Read Route is disabled, Write route must be enabled to a configuration that is not AXI RD -> AXI WR.
- If Read Route is disabled, Read Fixed field is ignored.
- If Write Route is disabled, Write Fixed field is ignored.
- Addresses and Byte Counts must be aligned to AXI data width (1 DWORD).
- Block Size is used only for reads from the Subsystem Recovery Interface. When block size has a non-zero value, the DMA will only issue AXI read requests when the payload available input signal is set to 1, and will limit the size of read requests to the value of block size. For all other transactions (such as AXI writes, or any single-dword access to a subsystem register via the DMA), block size shall be set to a value of 0 for every transaction.
- AES mode is only valid with Read/Write routes AXI RD -> AXI WR, Read/Write Fixed = 0, and Block Size 0.
Steps:
- Write Byte Count
- Write Block Size
- Write Source Addr (value is ignored if data is from AHB)
- Write Dest Addr (value is ignored if data is to AHB).
- To perform an accelerated SHA operation on incoming read data, firmware sets the Read/Write route to AXI RD-> AXI WR, and the destination address to the SoC address for the SHA Acceleration data in aperture.
- Set Interrupt Enables (optional)
- If Mailbox R/W: Acquire Mailbox Lock
- If SHA Acc Op:
- First acquire Sha Accel Lock via AXI by using this flow (with the AHB-> AXI WR route) to initiate AXI manager action
- Initiate Sha Accel streaming operation via AXI by using this flow (with the AHB-> AXI WR route) to initiate AXI manager action
- Run this operation with the AXI RD -> AXI WR route to move data from SoC location into Sha Accelerator
- If AES Mode:
- Fully configure AES with IV/KEY see AES spec for more details
- Stream in any header info to AES like AAD
- Set read/write routes to AXI
- Set AES_MODE in DMA
- Set aes_gcm_mode in DMA if this is the AES mode
- Set Control Register
- Set Read/Write Routes
- Set Read/Write Fixed=0/1
- GO
- (All 3 may be single write or separate, GO must be last bit to set)
- If AHB data: Wait for RD FIFO not empty or WR FIFO not full
- Push/Pop data (using Rd Data/Wr Data register offsets) until all requested bytes transferred
- If AHB Error β check status0 for Error, then check for βCommand Errorβ
- Wait for TXN Done Interrupt (or Poll Status0)
- Read Status0, confirm Busy=0, Error=0
Descriptor
https://chipsalliance.github.io/caliptra-rtl/main/internal-regs/?p=clp.axi_dma_reg
Caliptra Subsystem Fuse Controller
FUSE controller is an RTL module that is responsible for programming and reading the FUSEs. This module has an AXI interface that is connected to Caliptra Subsystemβs AXI interconnect. This module provides the device with one-time programming functionality, resulting in non-volatile programming that cannot be reversed. This functionality is delivered via an open-source FUSE Controller and a proprietary FUSE/OTP Macro. This RTL module manages the following FUSE partition mapping, which can be found in the Fuse Controller Memory Map.
Partition Details
The Fuse Controller is configured a total of 16 partitions (See Fuse Controller's Fuse Partition Map), while it can support different number of partitions based on SoC product requirements. Secret FUSE partitions are prefixed with the word "Secret" and are associated with specific Life Cycle (LC) states, such as "MANUF" or "PROD." This naming convention indicates the LC state required to provision each partition.
Key Characteristics of Secret Partitions
- Programming Access:
UDS_SEEDandFIELD_ENTROPYpartitions can only be programmed by the Caliptra Core.- Secret partitions are buffered, meaning they are stored in registers and are erased (temporarily zeroized) if Caliptra-SS enters debug mode.
- Locking Mechanism:
- Write access to a partition can be permanently locked when no further updates are required.
- To lock a partition, an integrity constant is calculated and programmed alongside the data for that partition.
Partition-Specific Behaviors
Life Cycle Partition
- The life cycle partition remains active across all stages and cannot be locked.
- This ensures that the device can transition back to the RMA state in case of unexpected failures during production.
Vendor Test Partition
- The vendor test partition is used for FUSE programming smoke checks during manufacturing.
- Unlike other partitions, ECC uncorrectable errors in this partition do not trigger fatal errors or alerts due to the nature of FUSE smoke checks, which may leave certain FUSE words in inconsistent states.
Locking the Validated Public Key Partition
During firmware authentication, the ROM validates the vendor public keys provided in the firmware payload. These keys, which support ECC, MLDSA, and LMS algorithms, are individually hashed and compared against stored fuse values (e.g., CPTRA_CORE_VENDOR_PK_HASH_n). Once a valid key is identified, the ROM locks that specific public key hash and all higher-order public key hash entries until the next cold reset. This ensures that the validated keyβs fuse entry remains immutable. Importantly, the locking mechanism is applied only to the public key hashes. The associated revocation bits, which allow for runtime key revocation, remain unlocked. To support this, the fuse controller (FC) implements two distinct partitions:
-
PK Hash Partition
- Purpose:
- Contains the
CPTRA_CORE_VENDOR_PK_HASH[i]registers for i ranging from 1 to N. - Once a key is validated, the corresponding hash and all higher-order hashes are locked by MCU ROM, making them immutable until a cold reset.
- Contains the
- Layout & Details:
- Partition Items:
CPTRA_CORE_VENDOR_PK_HASH[i]where i ranges from 1 to N.- Default N: 1
- Maximum N: 16
- Size: N Γ 384 bits (each hash is 384-bit)
- Programming:
- The first key (i=1) is programmed during the manufacturing phase.
- The remaining keys (if any, i.e., Nβ1) can be programmed during manufacturing or in the field (production).
- Partition Item:
CPTRA_CORE_VENDOR_PK_HASH_VALIDis used to indicate which of the N keys is valid. Therefore, the length is N to support N-bit hot-encoding.
- Partition Items:
- Purpose:
-
PK Hash Revocation Partition
- Purpose: This partition stores runtime-updateable revocation bits and PQC type information.
- Layout & Details:
- For each vendor public key (
VENDOR_PK_HASH[i]), the partition contains:- ECC Revocation Bits: 4 bits (e.g.,
CPTRA_CORE_ECC_REVOCATION[i]) - LMS Revocation Bits: 32 bits (e.g.,
CPTRA_CORE_LMS_REVOCATION[i]) - MLDSA Revocation Bits: 4 bits (e.g.,
CPTRA_CORE_MLDSA_REVOCATION[i]) - PQC Key Type Bits: 1-bit one-hot encoded selection (e.g.,
CPTRA_CORE_PQC_KEY_TYPE[i])
- ECC Revocation Bits: 4 bits (e.g.,
- Attributes:
- This partition is kept separate from the PK hash partition to allow for runtime updates even after the validated public key is locked.
- For each vendor public key (
-
Volatile Locking Mechanism
- To ensure that the validated public key remains immutable once selected, the FC uses a volatile lock mechanism implemented via the new register
otp_ctrl.VENDOR_PK_HASH_LOCK. - Once the ROM determines the valid public key (e.g., the 3rd key is selected), it locks the corresponding fuse entries in the PK hash partition.
- The lock is applied by writing a specific value to
otp_ctrl.VENDOR_PK_HASH_LOCK. - If the OCP L.O.C.K. is enabled, the same lock mechanism is also applied on
CPTRA_SS_LOCK_HEK_PROD_Xfuse patitions.-
Example:
// Lock the 3rd vendor public key hash and all higher order key hashes write_register(otp_ctrl.VENDOR_PK_HASH_LOCK, 0xFFF2); // This operation disables any further write updates to the validated public key fuse region. -
The ROM polls the
STATUSregister until the Direct Access Interface (DAI) returns to idle, confirming the completion of the lock operation. If any errors occur, appropriate error recovery measures are initiated. -
Once locked, the PK hash partition cannot be modified, ensuring that the validated public key remains unchanged, thereby preserving the secure boot chain.
-
If there needs to be update or programming sequence in PK_HASH set, it needs to be in ROM execution time based on a valid request. Therefore, requires cold-reset.
-
The PK hash revocation partition remains unlocked. This design allows the chip owner to update revocation bits and PQC type settings at runtime, enabling the dynamic revocation of keys without affecting the locked public key.
-
Hardware Integrity Checker
Once partitions are locked, the hardware integrity checker performs two primary integrity checks to ensure the consistency of the volatile buffer registers:
-
ECC Protection:
- All buffered partitions include additional ECC protection (8-bit ECC for each 64-bit block), which is monitored concurrently.
-
Digest Verification:
- The digest of each partition is recomputed at semi-random intervals and compared to the digest stored alongside the partition.
Purpose
These integrity checks verify whether the contents of the buffer registers remain consistent with the calculated digest. They do not verify the consistency between storage flops and the FUSE.
Notes
- Zeroization of Secret Partitions: Secret partitions are temporarily zeroized when Caliptra-SS enters debug mode to ensure security.
- Locking Requirement: After the device finishes provisioning and transitions into production, partitions that no longer require updates should be locked to prevent unauthorized modifications.
- Further Information: For more information about the conditional states, please refer to OpenTitan open-source silicon Root of Trust (RoT) project.
General Guidance
Reset Considerations
It is important to note that values in OTP can be corrupted if a reset/zeroization occurs during a programming operation. This should be of minor concern for SW, however, since all partitions except for the LIFE_CYCLE partition are being provisioned in secure and controlled environments, and not in the field. The LIFE_CYCLE partition is the only partition that is modified in the field - but that partition is entirely owned by the life cycle controller and not by SW.
Programming Already Programmed Regions
OTP words cannot be programmed twice, and doing so may damage the memory array. Hence the OTP controller performs a blank check and returns an error if a write operation is issued to an already programmed location.
Caliptra Subsystem Life Cycle Controller
It is an overview of the architecture of the Life-Cycle Controller (LCC) Module for its use in the Caliptra Subsystem. The LCC is responsible for managing the life-cycle states of the system, ensuring secure transitions between states, and enforcing security policies.
Caliptra Subsystem, SOC Debug Architecture Interaction
Figure below shows the Debug Architecture of the Caliptra Subsystem and some important high-level signals routed towards SOC. The table in Key Components and Interfaces section shows all the signals that are available to SOC (outside of Caliptra Subsystem usage).
Figure: Caliptra Subsystem & SOC Debug Architecture Interaction
Note: SoC Debug Architecture of the Caliptra Subsystem with LCC; the red dashed circles highlight the newly added blocks and signals.
The figure below shows the LCC state transition and Caliptra Subsystem enhancement on LCC state transitions. It illustrates the life cycle flow of Caliptra Subsystem.
Figure: Caliptra Subsystem Life Cycle Controller Summary

Note: Caliptra Subsystem life cycle flow. This flow shows legal state transitions in life cycle controller by excluding its invalid states for simplicity.
Caliptra Subsystem LCC State Definitions
| Name | Encoding | Description |
|---|---|---|
| RAW | FUSE | This is the default state of the FUSE. During this state, no functions other than transition to TEST_UNLOCKED0 are available. The token authorizing the transition from RAW to TEST_UNLOCKED0 is a value that is secret global to all devices. This is known as the RAW_UNLOCK token. |
| TEST_LOCKED{N} | FUSE | TEST_LOCKED{N} states have identical functionality to RAW state and serve as a way for the Silicon Creator to protect devices in transit. It is not possible to provision FUSE root secrets during this state. This is enforced by hardware and is implementation defined. To progress from a TEST_LOCKED state to another TEST_UNLOCKED state, a TEST_UNLOCKED token is required. |
| TEST_UNLOCKED{N} | FUSE | Transition from RAW state using token stored in FUSE. This state is used for manufacturing and production testing. During this state: CLTAP (chip level TAPs) is enabled; Debug functions are enabled; DFT functions are enabled. It is expected that LCC tokens will be provisioned into FUSE during these states. Once provisioned, these tokens are no longer readable by software. |
| MANUF | FUSE | Transition from TEST_UNLOCKED state using token stored in FUSE. This is a mutually exclusive state to PROD and PROD_END. To enter this state, MANUF_TOKEN is required. This state is used for developing provisioning and mission mode. In this state, UDS and Field Entropy FUSE partitions can be provisioned. During this state: CLTAP (chip level TAPs) is enabled; Debug functions are enabled; DFT functions are disabled |
| PROD | FUSE | Transition from MANUF state using token stored in FUSE. PROD is a mutually exclusive state to MANUF and PROD_END. To enter this state, PROD_TOKEN is required. This state is used both for provisioning and mission mode. During this state: CLTAP is disabled; Debug functions are disabled; DFT functions are disabled; Caliptra Subsytem can grant SoC debug unlock flow if the conditions provided in βSoC Debug Flow and Architecture for Production Modeβ section are satisfied. SoC debug unlock overwrites the signals and gives the following cases: CLTAP is enabled; Debug functions are enabled based on the defined debug policy; DFT is enabled but this DFT enable is called SOC_DFT_EN, which has less capabilities than DFT_EN granted in TEST_UNLOCKED. |
| PROD_END | FUSE | This state is identical in functionality to PROD, except the device is never allowed to transition to RMA state. To enter this state, a PROD_END token is required. Only transition to SCRAP mode is allowed. |
| RMA | FUSE | Transition from TEST_UNLOCKED / PROD / MANUF using token stored in FUSE. It is not possible to reach this state from PROD_END. If the RMA transition is requested, the request must follow the asserted RMA PPD pin. Without this pin, RMA request is discarded. See cptra_ss_lc_Allow_RMA_or_SCRAP_on_PPD_i in Caliptra Subsystem Integration Specification Document. When transitioning from PROD or MANUF, an RMA_UNLOCK token is required. When transitioning from TEST_UNLOCKED, no RMA_UNLOCK token is required. During this state: CLTAP is enabled; Debug functions are enabled; DFT functions are enabled |
| SCRAP | FUSE | Transition from any state. If the SCRAP transition is requested, the request must follow the asserted SCRAP PPD pin. Without this pin, SCRAP request is discarded. See cptra_ss_lc_Allow_RMA_or_SCRAP_on_PPD_i in Caliptra Subsystem Integration Specification Document. During SCRAP state the device is completely dead. All functions, including CPU execution are disabled. The only exception is the TAP of the life cycle controller which is always accessible so that the device state can be read out. No owner consent is required to transition to SCRAP. Note also, SCRAP is meant as an EOL manufacturing state. Transition to this state is always purposeful and persistent, it is NOT part of the deviceβs native security countermeasure to transition to this state. |
| INVALID | FUSE | Invalid is any combination of FUSE values that do not fall in the categories above. It is the βdefaultβ state of life cycle when no other conditions match. Functionally, INVALID is identical to SCRAP in that no functions are allowed and no transitions are allowed. A user is not able to explicitly transition into INVALID (unlike SCRAP), instead, INVALID is meant to cover in-field corruptions, failures or active attacks. |
Note
- The LCC performs state transitions in a forward-only manner. It begins in the RAW state and follows the sequence: TEST_UNLOCKED and TEST_LOCKED, then MANUF, then PROD, and finally either PROD_END or RMA. After transitioning to TEST_UNLOCKED, the LCC can branch to any of the following states: MANUF, PROD, PROD_END, or RMA. However, once the LCC transitions to PROD, PROD_END, or RMA, it cannot return to the MANUF state. Additionally, note that the LCC cannot branch to RMA from PROD_END.
DFT & DFD Life-cycle States
In addition to the decoding signals of the Life-Cycle Controller (LCC) proposed in the OpenTitan open-source silicon Root of Trust (RoT) project, we introduce new signals: SOC_DFT_EN as SOC_HW_DEBUG_EN. These signals are critical for managing the test and debug interfaces within the Caliptra Subsystem, as well as at the broader SoC level.
While this architecture document explains how the Caliptra Subsystem provides DFT and DFD mechanisms through the DFT_EN, SOC_HW_DEBUG_EN, and SOC_DFT_EN signals, it also offers greater flexibility by supporting various SoC debugging options through the broader SoC debug infrastructure. The architecture does not constrain the SoCβs flexibility with the core security states of Caliptra and LCC states. We establish a set of clear guidelines for how the Caliptra Subsystem transitions through the unprovisioned, manufacturing, and production phases, ensuring security. However, this architecture remains flexible, offering multiple levels of debugging options in production debug mode. Level 0 is designated for debugging the Caliptra Subsystem itself, while higher levels can be interpreted and customized by the SoC designer to implement additional debugging features. For instance, if the SoC designer wishes to include extra DFT and DFD capabilities, they can utilize one of the debug levels provided during production debug mode and expand its functionality accordingly. For more details, see βSoC Debug Flow and Architecture for Production Modeβ Section and βMasking Logic for Debugging Features in Production Debug Modeβ Section.
The DFT_EN signal is designed to control the scan capabilities of both the Caliptra Subsystem and the SoC. When DFT_EN is set to HIGH, it enables the scan chains and other Design for Testability (DFT) features across the system, allowing for thorough testing of the chip. This signal is already provided by the existing LCC module, ensuring compatibility with current test structures. However, the SoC integrator has the flexibility to assign additional functionality to one of the debugging options provided by the debug level signals during production debug mode. For example, at the SoC level, an integrator may choose to use one of these levels to enable broader SoC DFT features, allowing for system-level testing while maintaining Caliptra's protection. To enable SoC DFT signal, SoC needs to set SOC_DFT_EN signal HIGH by using the infrastructure defined in βMasking Logic for Debugging Features in Production Debug Modeβ Section. Note that SOC_DFT_EN has a different capabilities than DFT_EN signal. DFT_EN is contolled by LCC, while SOC_DFT_EN is controlled by MCI.
The SOC_HW_DEBUG_EN signal is a new addition that governs the availability of the Chip-level TAP (CLTAP). CLTAP provides a hardware debug interface at the SoC level, and it is accessible when SOC_HW_DEBUG_EN is set to HIGH. For further details on how this signal integrates with the overall system, refer to the βTAP Pin Muxingβ Section of this document.
In the manufacturing phase, the Caliptra Subsystem asserts SOC_HW_DEBUG_EN high, with the signal being controlled by the LCC. In PROD mode, this signal is low. However, the SoC integrator has the flexibility to enable CLTAP during production debug mode by incorporating additional logic, such as an OR gate, to override the SOC_HW_DEBUG_EN signal, like DFT_EN. This architecture provides a mask register that allows SoC to program/configure this overriding mechanism at integration time or using MCU ROM. This allows the SoC to maintain control over hardware debug access while preserving the intended security protections in production.
Table: LCC State and State Decoder output ports
LCC State and State Decoder Output Ports
| LCC State\Decoder Output | DFT_EN | SOC_DFT_EN | SOC_HW_DEBUG_EN |
|---|---|---|---|
| RAW | Low | Low | Low |
| TEST_LOCKED | Low | Low | Low |
| TEST_UNLOCKED | High | High | High |
| MANUF* | Low | TOKEN - CONDITIONED** | High |
| PROD* | Low | TOKEN - CONDITIONED** | TOKEN - CONDITIONED** |
| PROD_END | Low | Low | Low |
| PROD_END* | Low | TOKEN - CONDITIONED** | TOKEN - CONDITIONED** |
| RMA | High*** | High | High |
| SCRAP | Low | Low | Low |
| INVALID | Low | Low | Low |
| POST_TRANSITION | Low | Low | Low |
*: Caliptra can enter debug mode and update these signals even though LCC is in MANUF or PROD, PROD_END states. This case is explained in βHow does Caliptra Subsystem enable manufacturing debug mode?β and βSoC Debug Flow and Architecture for Production Modeβ.
**: SOC_DFT_EN and SOC_HW_DEBUG_EN can be high if Caliptra SS grants debug mode (either manufacturing or production). This case is explained in βHow does Caliptra Subsystem enable manufacturing debug mode?β and βSoC Debug Flow and Architecture for Production Modeβ. SOC_HW_DEBUG_EN is set high to open CLTAP and SOC_DFT_EN enables DFT by SoC design support. However, this condition also needs to go through the flow described in βSoC Debug Flow and Architecture for Production Modeβ. Caliptra Subsystem state should be set to either the manufacturing mode debug unlock or Level 0 of the production debug unlock.
***: RMA state enables DFT_EN but Caliptra Core enters the debug mode when LCC enters RMA state. Thus, Caliptra Core clears UDS and Field Entropy secrets.
TAP Pin Muxing
The LCC includes a TAP interface, which operates on its own dedicated clock and is used for injecting tokens into the LCC. Notably, the LCC TAP interface remains accessible in all life cycle states, providing a consistent entry point for test and debug operations. This TAP interface can be driven by either the TAP GPIO pins or internal chip-level wires, depending on the system's current configuration.
Figure: Caliptra Subsystem Life Cycle Controller Summary
Note: Above figure of TAP pin muxing block diagram with a conceptual representation. SOCs may implement this in their own way
SOC logic incorporates the TAP pin muxing to provide the integration support and manage the connection between the TAP GPIO pins and the Chip-Level TAP (CLTAP). As illustrated in figure above, this muxing logic determines the source that drives the LCC TAP interface. The selection between these two sources is controlled by the SOC_HW_DEBUG_EN signal. When SOC_HW_DEBUG_EN is set to high, control is handed over to the CLTAP, allowing for chip-level debug access through the TAP GPIO pins. Conversely, when SOC_HW_DEBUG_EN is low, the TAP GPIO pins take control, enabling external access to the LCC TAP interface.
LCC State and State Decoder Output Ports Table outlines the specific LCC states that enable the SOC_HW_DEBUG_EN signal. These states include TEST_UNLOCK, MANUF, PROD (debug unlocked version only), and RMA. In these states, the LCC allows internal chip-level debug access via CLTAP, thereby facilitating advanced debugging capabilities. This muxing approach ensures that the TAP interface is appropriately secured, and that access is granted only under specific conditions, aligning with the overall security and functional requirements of the Caliptra Subsystem. Note: It is important to note that CLTAP provides access to the TAP interfaces of the LCC, MCU, or Caliptra Core. This functionality is managed by SoC logic, not by Caliptra-SS. Consequently, the SoC integration effort should ideally include an additional mux after CTAP to route the connection to one of the following: the LCC TAP, MCU TAP, or Caliptra Core TAP.
TAP pin muxing also enables routing to Caliptra TAP. This selection happens when DEBUG_INTENT_STRAP is high. This selection is done through the GPIO and indicates that Caliptra will enter debug mode if the secret tokens are provided. Caliptra Subsystem has two debug modes: manufacturing debug and production debug. Entering these debug flows are explained in the following sections: How does Caliptra Subsystem enable manufacturing debug mode?, SoC Debug Flow and Architecture for Production Mode and Masking Logic for Debugging Features in Production Debug Mode
Note: The Caliptra TAP can run exact flow with AXI, targeting the mailbox and SOC provides that interface to platform.
How does Caliptra Subsystem enable manufacturing debug mode?
The following figure illustrates how Caliptra Subsystem enters the manufacturing debug mode. To enter this mode, LCC should be in MANUF state. While being in manufacturing debug mode, LCC does not change its state from MANUF to any other state. During the MANUF state, Caliptra Subsystem can enable manufacturing debug flow by following steps:
Figure: Caliptra Subsystem Manuf Debug Life Cycle View
Note: The flow diagram on the right side shows the LCC states (grey boxes) and their transitions, while the flow diagram on the left illustrates Caliptra SSβs enhancements to the LCC for the manufacturing phase. Specifically, the flow on the left depicts the routine for entering manufacturing debug mode.
Flow Explanation
- (Platform) Assert BootFSMBrk : Temporarily halting the Caliptra HW bootfsm boot process to allow for secure operations like token injection and verification.
- (Platform) Assert DEBUG_INTENT_STRAP: If Caliptra core HW samples this pin as high and sees that LCC is in MANUF mode, it prepares itself for entering debug mode. Once the DEBUG_INTENT_STRAP is detected, Caliptra HW immediately wipes out its secret assets, including the Unique Device Secret (UDS) and Field entropy, ensuring that no sensitive data remains accessible. If this Pin is not high, Caliptra continues always in non-debug mode without taking any further action listed with the following states.
- (Platform over TAP) MANUF_DEBUG_UNLOCK_REQ: The system intends to be in debug mode for manufacturing services.
- (Platform over TAP) Write to Boot Continue: Resuming the boot process after halting it with BootFSMBrk.
- ROM reads MANUF_DEBUG_UNLOCK_REQ at optimal time for ROM and clears mailbox lock and asserts MANUF_DEBUG_UNLOCK_IN_PROGRESS.
- Platform over TAP requests Caliptra mailbox lock. If lock is obtained, then platform over TAP writes to TAP mailbox to inject Token via TAP Register Write: Enabling the injection of a token through TAP and writing to a specific register.
- ROM Executes Hash Hashing: Calculates the hash value of the injected token using a SHA-512 hash core. Before starting this operation.
- Token Comparison: A constant-time comparison between the authentication values of injected token and the FUSE content.
- ROM Drives MANUF_DEBUG_UNLOCK Signal: The ROM writes to a register that controls the MANUF_DEBUG_UNLOCK signal based on the result of the token verification.
Note: The manufacturing debug flow relies on the hash comparison hardness rather than an authentication check provided with an asymmetric cryptography scheme. However, the following sections show that production debug flow relies on asymmetric cryptography scheme hardness. The reason behind this choice is that the manufacturing phase is the early phase of the delivery, and this phase is entered in an authorized entityβs facility. Therefore, this architecture keeps the manufacturing phase simpler.
Production Debug Unlock Architecture
The Caliptra Subsystem includes SoC debugger logic that supports Caliptraβs production debug mode. This debugger logic extends the capabilities of the Lifecycle Controller (LCC) by providing a production debug mode architecture that the LCC does not inherently support, except in the RMA state. This architecture manages the initiation and handling of the production debug mode separately from the LCC's lifecycle states.
The process of enabling production debug mode begins when the DEBUG_INTENT_STRAP pin is asserted high via the SoCβs GPIO. This pin signals Caliptra to start the debug mode when the LCC is in the PROD and PROD_END states. Before the debug unlock flow, the MCU reads all hashed public keys from the fuse macros and writes them to the SS_PROD_DEBUG_UNLOCK_AUTH_PK_HASH_REG_BANK registers. Additionally, the MCU sets the number of public keys used for production debug unlock by writing to the SS_NUM_OF_PROD_DEBUG_UNLOCK_AUTH_PK_HASHES register. The value DEBUG_AUTH_PK_HASH_REG_BANK_OFFSET represents an address offset, while NUM_OF_DEBUG_AUTH_PK_HASHES defines how many public keys are available for reading. These two values establish the debug policy depth for different debugging levels.
Overview of Debug Unlock Initiation
The process begins when the platform sets three conditions:
- The
DEBUG_INTENTfield in theSS_DEBUG_INTENTregister. - The
PROD_DEBUG_UNLOCK_REQbit in theSS_DBG_MANUF_SERVICE_REG_REQregister. - The
CPTRA_BOOT_GOallows Caliptra Core to continue its execution. On reset, the Caliptra ROM checks these two signals. If both are asserted, the ROM begins the production debug unlock process. Upon detecting a valid debug intent: - Caliptra hardware erases its secret assets, including the Unique Device Secret (UDS) and Field Entropy, before exposing any debug interfaces, ensuring sensitive data is irreversibly destroyed.
- Caliptra ROM sets the
TAP_MAILBOX_AVAILABLEandPROD_DBG_UNLOCK_IN_PROGRESSbits in theSS_DBG_MANUF_SERVICE_REG_RSPregister.
Note: Similar to the manufacturing debug flow, BootFSMBrk must be asserted to halt Caliptra ROM execution. Otherwise, the ROM may advance past the debug request before PROD_DEBUG_UNLOCK_REQ is populated. BootFSMBrk is a prerequisite for entering the debug flow ONLY if Caliptra core ROM needs to be debugged; for run-time injection of prod debug unlock token, setting BootFSMBrk is not required.
Secure Debug Unlock Protocol
- The production debug unlock flow uses a challenge-response authentication model involving public key verification and hybrid cryptography (ECC and MLDSA). The flow includes the following steps:
- AUTH_DEBUG_UNLOCK_REQ Command: Caliptra ROM polls the Mailbox interface, awaiting an
AUTH_DEBUG_UNLOCK_REQfrom the platform. - The request payload includes:
- AUTH_DEBUG_UNLOCK_REQ Command: Caliptra ROM polls the Mailbox interface, awaiting an
| Field | Size (bytes) | Description |
|---|---|---|
| Length | 4 | Must be 2 DWORDs. |
| Unlock Level | 1 | Requested unlock level. |
| Reserved | 3 | Reserved. |
- If the payload is invalid, Caliptra ROM:
- Sets the
PROD_DBG_UNLOCK_FAILbit. - Clears the
PROD_DBG_UNLOCK_IN_PROGRESSbit. - Exits the debug unlock process.
- Sets the
- Upon successful initial validation, Caliptra ROM sends back an
AUTH_DEBUG_UNLOCK_CHALLENGEpayload:
| Field | Size (bytes) | Description |
|---|---|---|
| Length | 4 | Should be 21 DWORDs. |
| Unique Device Identifier | 32 | Caliptra device identifier. |
| Challenge | 48 | Randomly generated nonce. |
- The platform signs the challenge data and sends an
AUTH_DEBUG_UNLOCK_TOKEN:
| Field | Size (bytes) | Description |
|---|---|---|
| Length | 4 | Should be 0x753 DWORDs. |
| Unique Device Identifier | 32 | Echoed from challenge. |
| Unlock Level | 1 | Echoed from initial request. |
| Reserved | 3 | Reserved. |
| Challenge | 48 | Echoed from challenge. |
| ECC Public Key | 96 | P-384 public key. |
| MLDSA Public Key | 2592 | MLDSA-87 public key. |
| ECC Signature | 96 | Signature of challenge message. |
| MLDSA Signature | 4628 | Signature of challenge message. |
- Finally, Caliptra ROM reads the expected SHA2-384 public key hash from the corresponding register by using the following equation:
- SS_PROD_DEBUG_UNLOCK_AUTH_PK_HASH_REG_BANK_OFFSET + ((Unlock Level - 1) * 48 bytes)
- If hash of
AUTH_DEBUG_UNLOCK_CHALLENGE's MLDSA and ECC public keys match with the read value fromSS_PROD_DEBUG_UNLOCK_AUTH_PK_HASH_REG_BANK_OFFSET, ROM uses these MLDSA and ECC public keys to perform the authentication. - Upon successful validation:
- Sets the
PROD_DBG_UNLOCK_SUCCESSbit. - Writes the
CALIPTRA_SS_SOC_DEBUG_UNLOCK_LEVELregister granted debug level with a one-hot encoding result (1 << (Unlock Level - 1)). - Clears the
TAP_MAILBOX_AVAILABLEbit. - Clears the
PROD_DBG_UNLOCK_IN_PROGRESSbit. - Exits the debug unlock process.
- Sets the
Granting Production Debug Mode:
- If authentication succeeds, Caliptra ROM does not immediately grant full production debug mode. Instead, the ROM sets the appropriate "debug level" signal, which corresponds to the type of debug access being requested.
- Caliptra ROM writes CALIPTRA_SS_SOC_DEBUG_UNLOCK_LEVEL register, which will be wired to the specific debug enable signal. This signal is part of an N-wide signal that is mapped to the payload encoding received during the debug request. N is defined by NUM_OF_DEBUG_AUTH_PK_HASHES. The default version of N is 8. The payload encoding can either be one-hot encoded or a general encoded format, and this signal is passed to the SoC to allow it to make the final decision about the level of debug access that should be granted. In Caliptraβs subsystem-specific implementation, the logic is configured to handle one-hot encoding for these 8 bits. The level 0 bit is routed to both Caliptra and the MCU TAP interface, allowing them to unlock based on this level of debug access. This granular approach ensures that the system can selectively unlock different levels of debugging capability, depending on the payload and the authorization level provided by the debugger.
Granting Production Debug Mode with Caliptra Runtime FW: Although the flow above describes ROM-based authentication, the same challengeβresponse mechanism may be executed by Caliptra Runtime Firmware. In this case, RT FW performs the challenge-response authentication steps via mailbox commands, which will be used to update the debug unlock level. The DEBUG_INTENT_STRAP must still be asserted high before Caliptra subsystem is out of reset. This ensures that even RT-based debug enablement remains gated by hardware intent and cannot be triggered purely through software.
Masking Logic for Debugging Features in Production Debug Mode (MCI)
In the production debug mode, the SoC can enable certain debugging featuresβsuch as DFT_EN and SOC_HW_DEBUG_ENβusing a masking mechanism implemented within the Manufacturer Control Interface (MCI). This masking infrastructure allows the SoC to selectively control debug features that are normally gated by Caliptraβs internal signals. The masking logic functions as a set of registers, implemented in the MCI, that can be written by the SoC to override or enable specific debugging functionalities in production debug mode. The masking registers in the MCI act as an OR gate with the existing debug signals. For instance, if DFT_EN is controlled by Caliptra, the SoC can assert the corresponding mask register to enable Design for Test (DFT) capabilities in the SoC, even if Caliptra has not explicitly enabled it. Similarly, the SOC_HW_DEBUG_EN signal can be masked through the MCI registers, giving the SoC the flexibility to unlock TAP interfaces and provide the required debugging in production.
Figure: Caliptra Subsystem Infrastructure for SOC flexibility to override various debug signals

This mechanism is only authorized when both the LCC and Caliptra core are in the PROD state and operating in production debug mode. The masking logic ensures that these features are enabled securely, in line with the production debug flow described in the "SoC Debug Flow and Architecture for Production Mode" section. By leveraging the MCIβs masking infrastructure, the SoC integrator has greater flexibility to implement custom debugging options during production without compromising the security framework established by Caliptra.
**Note: Unlike the production state, the manufacturing state supports only a single debug level. Although it uses the same masking register to overrideβ―DFT_EN, only the registerβs least significant bit is used in manufacturing debug mode.
LCC Interpretation for Caliptra βCoreβ Security States
Caliptra Core has five security states as given with the figure below (Copied from Caliptra core specification). Three of these states enable debugging with different features, while two of these states are in non-debug mode. Caliptra Subsystem implements gasket logic to translate the LCC states to Caliptra βCoreβ security states.
Figure: Caliptra Core Security States

This translation is essential for ensuring that the system behaves correctly according to its current lifecycle stage, whether it is in a development phase, production, or end-of-life states. The LCC provides specific decoding signalsβDFT_EN, SOC_DFT_EN, SOC_HW_DEBUG_ENβthat accommodates the debug capabilities and security states of the Caliptra Core. Depending on the values of these signals, the Caliptra Core transitions between different security states as follows:
Non-Debug Mode: This state is enforced when the LCC is in RAW, TEST_LOCKED, SCRAP, INVALID, or POST_TRANSITION states. In these states, the DFT_EN, SOC_DFT_EN, SOC_HW_DEBUG_EN signals are all low, ensuring that no debug functionality is available. Caliptra remains in a secure, non-debug state, with no access to debugging interfaces.
Unprovisioned Debug Mode: When the LCC is in the TEST_UNLOCKED state, the DFT_EN, SOC_DFT_EN, SOC_HW_DEBUG_EN signals are all set high, enabling debug functionality. In this mode, the Caliptra Core allows extensive debugging capabilities, which is typically used during early development and bring-up phases.
Manufacturing Non-Debug Mode: This state occurs when the LCC is in the MANUF state, with SOC_HW_DEBUG_EN high and DFT_EN and SOC_DFT_EN low. In this state, the secrets have been programmed into the system, and the Caliptra can generate CSR (Certificate Signing Request) upon request. However, it remains in a secure, non-debug mode to prevent reading secrets through the debugging interfaces.
Manufacturing Debug Mode: Also occurring in the MANUF state, this mode is enabled when SOC_HW_DEBUG_EN is high. Here, the Caliptra Core provides debugging capabilities while maintaining security measures suitable for manufacturing environments. In this state, DFT_EN is low. However, SOC_DFT_EN can be set high if LSB of MCI_REG_SOC_DFT_EN_0 is set to high in MANUF debug state MCI-LCC-Signal-Masking.
Production Non-Debug Mode: This state is active when the LCC is in the PROD or PROD_END states, with all debug signals (DFT_EN, SOC_HW_DEBUG_EN) set to low. The Caliptra Core operates in a secure mode with no debug access, suitable for fully deployed production environments.
Production Debug Mode: This state is active when the LCC is in the PROD or PROD_END states, with debug DFT_EN, SOC_HW_DEBUG_EN set to low. Caliptra Core provides debugging capabilities while maintaining security measures suitable for manufacturing environments. However, SOC_DFT_EN and SOC_HW_DEBUG_EN can be set high with MCI masking registers MCI-LCC-Signal-Masking.
Production Debug Mode in RMA: In the RMA state, all debug signals are set high, allowing full debugging access. This state is typically used for end-of-life scenarios where detailed inspection of the system's operation is required.
The table below summarizes the relationship between the LCC state, the decoder output signals, and the resulting Caliptra βCoreβ security state:
Table: LCC state translation to Caliptra "Core" security states
| LCC State vs Decoder Output | DFT_EN | SOC_DFT_EN | SOC_HW_DEBUG_EN | Caliptra βCoreβ Security States |
|---|---|---|---|---|
| RAW | Low | Low | Low | Prod Non-Debug |
| TEST_LOCKED | Low | Low | Low | Prod Non-Debug |
| TEST_UNLOCKED | High | High | High | Unprovisioned Debug |
| MANUF | Low | Low | High | Manuf Non-Debug |
| MANUF* | Low | High** | High | Manuf Debug |
| PROD | Low | Low | Low | Prod Non-Debug |
| PROD* | Low | High** | High** | Prod Debug |
| PROD_END | Low | Low | Low | Prod Non-Debug |
| PROD_END* | Low | High** | High** | Prod Debug |
| RMA | High | High | High | Prod Debug |
| SCRAP | Low | Low | Low | Prod Non-Debug |
| INVALID | Low | Low | Low | Prod Non-Debug |
Note: In RAW, TEST_LOCKED, SCRAP and INVALID states, Caliptra βCoreβ is not brought out of reset. *: These states are Caliptra SSβs extension to LCC. Although the LCC is in either MANUF or PROD, PROD_END states, Caliptra core can grant debug mode through the logics explained in βHow does Caliptra Subsystem enable manufacturing debug mode?β and βSoC Debug Flow and Architecture for Production Modeβ. **: SOC_HW_DEBUG_EN and DFT_EN can be overridden by SoC support in debug mode of MANUF and PROD, PROD_END states.
Exception: Non-Volatile Debugging Infrastructure and Initial RAW State Operations
The Caliptra Subsystem features a non-volatile debugging infrastructure that allows the SoC integrator to utilize a dedicated SRAM for early-stage debugging during the VolatileUnlock state. This SRAM provides a means to store executable content for the MCU, enabling early manufacturing debug operations.
It is important to note that the VolatileUnlock state is not a standard LCC state but rather a temporary state. Unlike other LCC states, the LCC can transition to this state without requiring a power cycle, provided the necessary global VolatileUnlock token is passed. This token is unique in that it is not stored in the FUSE partition but is instead a global netlist secret.
Once in the VolatileUnlock state, the MCU can execute instructions stored in the SRAM, which can be initialized through a backdoor mechanism. This capability allows for basic FUSE tests and calibration operations during early debugging. However, the VolatileUnlock state is inherently unstable and temporaryβif a reset is applied or the Caliptra Subsystem undergoes a power cycle, the LCC will return to the RAW state.
LCC RAW Unlock Token Generation & Setting
The hashed RAW unlock token is stored as a netlist constant. Its value is determined by the
cptra_ss_raw_unlock_token_hashed_i input of caliptra_ss_top. Every integrator must set the
hashed token to a unique value. tools/scripts/lc_ctrl_script/gen_lc_ctrl_token.py can be used to
generate the hashed token. The unhashed RAW unlock token is a secret that must be obtained from
a cryptographically secure random number generator. The aforementioned tool accepts an unhashed
token as input or, if omitted, generates an unhashed token using Python's secrets module.
The hashed RAW unlock token must be applied to the cptra_ss_raw_unlock_token_hashed_i input of
caliptra_ss_top. It's also required in SW that performs a volatile RAW unlock.
The unhashed RAW unlock token is required in SW that performs a non-volatile RAW unlock.
SOC LCC Interface usage & requirements
The interaction between the SoC and the LCC within the Caliptra Subsystem is pivotal for maintaining the security and functionality of the overall system. This section outlines the specific usage and requirements for interfacing with the LCC from the SoC perspective.
SOC_HW_DEBUG_EN Utilization: The SOC_HW_DEBUG_EN signal plays a crucial role in controlling access to the Chip-Level TAP (CLTAP). When SOC_HW_DEBUG_EN is asserted high, it enables the exposure of registers to the TAP interface that are deemed secure for access during debugging. This is particularly important when CLTAP is open, as it allows authorized debugging operations while ensuring that sensitive or secure information remains protected.
LCC Outputs for Security/Debug Purposes: Beyond the primary debug controls, all other LCC outputs, as listed in the signal table below, are available for use by the SoC for any security or debugging purposes. These outputs provide the SoC with additional control signals that can be leveraged to enhance system security, monitor debug operations, or implement custom security features.
Integration with Clock/Voltage Monitoring Logic: If the SoC includes clock or voltage monitoring logic, it is recommended that these components be connected to the LCC's escalation interface. By integrating with the LCC, the SoC can ensure that any detected anomalies, such as voltage fluctuations or clock inconsistencies, trigger appropriate secure transitions as defined in this document. This connection enhances the overall security posture by enabling the LCC to respond dynamically to potential threats or system irregularities.
These requirements ensure that the SoC and LCC work in tandem to maintain a secure environment, particularly during debugging and system monitoring. By adhering to these guidelines, the SoC can leverage the LCCβs capabilities to protect sensitive operations and enforce security policies across the system.
LCC Module: Summarized Theory of operation
The LCC is designed to handle the various life-cycle states of the device, from initial provisioning through manufacturing to production and eventual decommissioning (RMA). It provides mechanisms for state transitions, secure key management, and debug control, ensuring that the device operates securely throughout its lifecycle. For more information, please refer to the Life-cycle controller documentation. While the LCC is reused from OpenTitan, Caliptra Subsystemβs security & debug architecture has some differences. Therefore, Subsystem implements the functions required to meet Caliptra specific requirements.
LCC has the same power up sequence as described in Life cycle controller documentation of OpenTitan open-source silicon Root of Trust (RoT) project. After completing the power up sequence, the LCC enters the normal operation routine. During this phase, its output remains static unless specifically requested to change. The LCC accepts life-cycle transition change requests from its TAP interface (TAP Pin Muxing). There are two types of state transitions: (i) unconditional transitions and (ii) conditional transitions.
For unconditional transitions, the LCC advances the state by requesting an FUSE update to the FUSE controller. Once the programming is confirmed, the life cycle controller reports a success to the requesting agent and waits for the device to reboot.
For conditional transitions, the LCC can also branch different states (TEST_UNLOCK, MANUF, PROD, PROD_EN, RMA) based on the received token through the TAP interface and compared the received tokens against the ones stored in FUSE . This branching is called conditional transitions. For more information about the conditional states, please refer to OpenTitan open-source silicon Root of Trust (RoT) project.
Notes:
- Some tokens are hardcoded design constants (specifically for RAW to TEST_UNLOCK), while others are stored in FUSE.
- Conditional transitions will only be allowed if the FUSE partition holding the corresponding token has been provisioned and locked.
- For transition counter limits and token hashing mechanism, please refer to OpenTitan open-source silicon Root of Trust (RoT) project [1].
- The LCC enters the post transition handling routine after completing conditional and unconditional transitions. During this routine, the LCC disables all of its decoded outputs and puts the system in an inert state.
Manufacturer Control Unit (MCU)
MCU is an instance of VeeR EL2 core. The following features are enabled on MCU - iCache, dedicated DCCM, AXI interfaces with seperate AXI USER ID to ROM and MCI, bit manipulation extensions that we have for Caliptra core (Zba, Zbb, Zbc, Zbs) and PMP + SMEPMP with max PMP entries. Please see Caliptra subsystem integration spec for the configuration options available at subsystem level.
If the MCU VeeR instance must be regenerated with new configuration, a shell script is available in the Caliptra Subsystem repository. The script is located at tools/scripts/veer_build_command.sh.
Manufacturer Control Interface (MCI)
Overview
The Manufacturer Control Interface (MCI) is a critical hardware block designed to supplement the Manufacturer Control Unit (MCU) within a System on Chip (SoC). The primary functions of the MCI include providing an SRAM bank, facilitating restricted communication through a mailbox from external entities, and managing a bank of Control/Status Registers (CSRs). Additionally, the MCI incorporates a Watchdog Timer and a Boot Sequencing Finite State Machine (FSM) to manage timing and control during the SoC boot sequence after power application. This boot sequence encompasses reset deassertion, initialization of the Fuse Controller, initialization of the Lifecycle Controller, and enabling the JTAG block for debugging and manufacturing processes.
The following diagram illustrates the internal components of the MCI.

MCI Feature Descriptions
Control/Status Registers (CSRs)
The Control/Status Registers (CSRs) within the MCI are designed to provide critical control and status monitoring functions for the SoC. These registers include configuration settings, status indicators, and control bits that allow communication and management of the various operations of the MCI. The CSR bank is accessible via the AXI interface and is mapped into the memory space to facilitate straightforward access and manipulation.
MCI CSR Access Restrictions
Certain registers within the CSR bank have write access restrictions based off of:
- AXI User
- Lock bits (SS_CONFIG_DONE, CAP_LOCK, etc.)
The privilaged users for the MCI CSRs are:
- MCU
- MCI SOC Config User (MSCU)
- MCU SRAM Config User (MSRCU)
All of these AXI Users come from straps and are not modifiable by SW. MCU is given the highest level of access and is expected to configure MCI registers and lock the configuration with various SS_CONFIG_DONE and LOCK bits. It also has access to certain functionalality like timers that are needed by the SOC but are critical for MCU functionality.
The MSCU is meant as a secondary config agent if the MCU is unable to configure MCI. Example when in the no ROM config it is expected the MCSCU can configure and lock down the MCI configuration. MSCU can be disabled by setting the strap to 0x0. For debug the MSCU strap can be set to all 1s meaning every access will give this level of privilege. The MCRCU populates the MCU FW update in MCU SRAM. There are a few registers within the MCI register bank it has special access to for facilitating the FW update. The registers can be split up into a few different categories:
| Write restrictions | Description |
|---|---|
| MCU Only | These registers are only meant for MCU to have access. There is no reason for SOC or the MCI SOC Config User to access the. Example is the MTIMER |
| MCU or MSCU | Access restricted to trusted agents but not locked. Example:RESET_REQUEST for MCU. |
| MCU or MSRCU | Access restricted to trusted agents but not locked. Example:RESET_REASON for MCU. |
| MCU or MSCU and CONFIG locked | Locked configuration by MCU ROM/MSCU and configured after each warm reset |
| Sticky MCU or MSCU and CONFIG locked | Locked configuration by MCU ROM/MSCU and configured after each cold reset |
| Locked by SS_CONFIG_DONE_STICKY | Configuration once per cold reset. |
| Locked by SS_CONFIG_DONE | Configuration once per warm reset. |
| MCU or MSCU until CAP_LOCK | Configured by a trusted agent to show HW/FW capabilieds then locked until next warm reset |
| MBOX_USER_LOCK | Mailbox specific configuration locked by it's own LOCK bit. Configured afer each arem reset. |
MCI Straps
All MCI straps shall be static before mci_rst_b is deasserted. Some straps have further restrictions as described below.
MCI has the following types of straps:
| Strap Type | Sampled or Direct Use | Description |
|---|---|---|
| Non-configurable Direct | Direct | Used directly by MCI and not sampled at all. These shall be constant non-configurable inputs to CSS/MCI. Inside MCI these are not overridable by SW. |
| Non-configurable Sampled | Sampled* | Sampled once per cold boot and not overridable by SW |
| Configurable Sampled | Sampled** | Sampled once per warm boot and SW can override via MCI Register Bank until SS_CONFIG_DONE is set. |
*NOTE: Strap sampling occurs when mci_rst_b is deasserted and is typically performed once per cold boot. This process is controlled by the SS_CONFIG_DONE_STICKY register; when set, sampling is skipped. If a warm reset happens before SS_CONFIG_DONE_STICKY is set, the straps will be sampled again, although this is not the usual behavior.
**NOTE: Strap sampling occurs when mci_rst_b is deasserted.
| Strap Name | Strap Type | Description |
|---|---|---|
strap_mcu_lsu_axi_user | Non-configurable Direct | MCU Load Store Unit AXI User. Given Special Access within MCI. |
strap_mcu_ifu_axi_user | Non-configurable Direct | MCU Instruction Fetch Unit AXI User. given special access within MCI. |
strap_mcu_sram_config_axi_user | Non-configurable Direct | MCU SRAM Config agent who is given special access to MCU SRAM Execution region to load FW image. Typically set to Caliptra's AXI User. |
strap_mci_soc_config_axi_user | Non-configurable Direct | MCI SOC Config User (MSCU). AXI agent with MCI configuration access. |
strap_mcu_reset_vector | Configurable Sampled | Default MCU reset vector. |
ss_debug_intent | Non-configurable Sampled | Provides some debug access to MCI. Show the intent to put the part in a debug unlocked state. Although not writable by SW via AXI. This is writable via DMI. |
Subsystem Boot Finite State Machine (CSS-BootFSM)
The Boot Sequencer FSM is responsible for the orderly and controlled boot process of the Caliptra Subsystem. This state machine ensures that all necessary initialization steps are completed in the correct sequence after power application. The boot sequence includes MCU and Caliptra Reset deassertions, Fuse Controller Initialization, and Lifecycle Controller Initialization.
The following boot flow explains the Caliptra subsystem bootFSM sequence.
Note: SOC may have other HW FSM steps that were done before Caliptra (CSS) is brought out of reset such as locking a PLL or calibrating a CRO, setting up GPIOs, test logic bring up etc. using SOC HW FSM.
-
SOC asserts Caliptra SS powergood and desserts Caliptra SS reset to MCI
a. SOC may choose to connect the same signals to the AXI fabric or bring it out of reset using a different signals. But the requirement is that before MCU is out of reset, fabric should be operational.
-
CSS-BootFSM will sample straps in MCI and will drive its outputs to reset defaults.
c. Note: In allowed LCC states, MCU TAP will be available when MCI is brought out of reset. Uncore registers in MCI will be available, but Core registers within MCU will only be available when MCU reset is deasserted.
-
CSS-BootFSM will go through Caliptra Subsystem Fuse controller (CSS FC) Init Sequence Init Sequence/handshakes
-
CSS-BootFSM will go through Caliptra Subsystem Life Cycle Controller (CSS LCC) Init Sequence/handshakes
a. SOC Note: Note that LCC shall start on an SOC generated internal clock to prevent clock stretch attacks
b. SOC Note: If the life cycle state is in RAW, TEST* or RMA, and if TRANSITION_CTRL.EXT_CLOCK_EN is set to one, the CLK_BYP_REQ signal is asserted in order to switch the main system clock to an external clock signal. This functionality is needed in certain life cycle states where the SOC internal clock source may not be fully calibrated yet, since the FUSE macro requires a stable clock frequency in order to reliably program the fuse array. Note that the TRANSITION_CTRL.EXT_CLOCK_EN register can only be set to one if the transition interface has been claimed via the CLAIM_TRANSITION_IF mutex. This function is not available in production life cycle states.
-
If MCU-No-ROM-Config strap is not set, then CSS-BootFSM will bring MCU out of reset and MCU ROM will start executing.
a. Note: MCU ROM may be used by some SOCs for doing additional SOC specific initializations.An example of such a SoC construction is MCI, MCU, CSS Fabric are running on external clock initially. MCU brings up PLL, some GPIO peripherals, does I3C init sequence etc and then performs clock switch to internal PLL clock domain so that the fabric is running on the internal clock domain before secrets are read on it from the fuse controller.
-
If MCU-No-ROM-Config is not set, MCU ROM will bring Caliptra out of reset by writing a MCI register (CPTRA_BOOT_GO)
-
If MCU-No-ROM-Config is set, CSS-BootFSM waits for a Caliptra GO write from SOC to bring Caliptra out of reset.
a. Note: MCU Reset Vector will be strapped to the MCU SRAM executable location at integration time.
b. Note: MCI will allow "MCI SOC CONFIG" AXI User to configure MCI registers MCU typically configures.
b. Note: MCI will allow any AXI User to write into SRAM if the LCC & debug state allows. SOC will have flexibility to implement desired logic to write to MCU SRAM to skip MCU ROM and a register bit to bring MCU out of reset. MCU will start executing from the reset vector that was strapped which enables the first fetch vector to access MCI SRAM
-
Caliptra reset (cptra_rst_b) is deasserted.
-
Caliptra BootFSM will go through its own boot flow as documented in Caliptra spec, reads secret fuses and sets βready_for_fusesβ to MCI.
Note: In MCU-NO-ROM-CONFIG, steps 11 through 14 & steps 16 through 18 are done by a SOC entity. Otherwise, MCU ROM will do the below steps.
-
MCU ROM will be polling for this indication
-
MCU ROM will now read FCβs SW partition for all the required fuses including its own and also write Caliptra fuses. Note that only non-secret fuses are accessible for MCU ROM by fuse controller construction. a. Note: All fuses will be zero if FC is not programmed
-
MCU ROM will also write owner_pk_hash register (and any other additional pre-ROM configuration writes here)
-
MCU ROM will do a fuse_write_done write to Caliptra
-
Caliptra ROM starts to execute from here on.
-
Once Caliptra populates MCU SRAM, it will set FW_EXEC_CTL[2] which will trigger a reset request to MCU. MCU either needs to enable interrupts or poll on the appropriate bit to detect that Caliptra has requested a reset.
-
CSS-BootFSM waits for MCU to request the reset. Then CSS-BootFSM will do a halt req/ack handshake with MCU and assert the MCU reset after the MCU reports that it has successfully halted.
-
MCU ROM will read the reset reason in the MCI and execute from MCU SRAM

Watchdog Timer
The Watchdog Timer within the MCI is a crucial component designed to enhance the reliability and robustness of the SoC. This timer monitors the operation of the system and can trigger a system reset if it detects that the system is not functioning correctly. The Watchdog Timer is configurable through CSRs and provides various timeout periods and control mechanisms to ensure the system operates within defined parameters. The Watchdog timer contains two different timers. These timers can be configured in two different modes:
- Cascade
- Independent
In cascade mode when the first timer reaches a timeout two things will happen:
- An error interrupt will be triggered
- Timer 2 will start counting
If the WDT is not serviced before Timer 2 times out two things happen:
- NMI output pin is asserted
- NMI HW_ERROR_FATAL is triggered which can assert an error_fatal on the MCI port.
In Independent mode the two timers are completely independent of each other. When they timeout an error interrupt will be asserted. The NMI will never be asserted while in independent mode.
MCU Mailbox
There are 2 mailboxes in the MCI. Each Mailbox component of the MCI allows for secure and restricted communication between external SoC entities and the MCU. This communication channel is essential for exchanging control messages, status updates, and other critical information that the MCU will use to monitor system boot, firmware updates, and security critical operations. Mailboxes are designed to ensure that only authorized entities can access and communicate through it, preserving the integrity and security of the SoC operations.
MCU Mailbox Limited Trusted AXI users
There are 5 trusted AXI Users determined at build time via parameters or via MCI lockable registers. These users, a default user 0xFFFF_FFFF, and MCU are the only AXI users that can access or obtain a lock on the mailbox.
Any untrusted AXI user trying to read or write the mailbox will receive an AXI error response (MCU Mailbox Errors).
Trusted users always have read access to the CSRs in the mailbox, but require a lock to write the CSRs or read/write the SRAM.
MCU Mailbox Locking
A Requester will read the "LOCK" register to obtain a lock on the mailbox. This is a read-set register, the lock is acquired when read returns 0. The Requester must be a Trusted user. Once the lock is obtained the Requestor has read access to the entire mailbox and write access to:
- All mailbox registers except the following will be RO:
- CMD_STATUS
- TARGET_STATUS
- TARGET_DONE
- TARGET_USER
- Mailbox SRAM Unlocking/releasing occurs when the requestor writes 0 to the EXECUTE register. After releasing the mailbox the SRAM is zeroed out(MCU Mailbox SRAM Clearing).
On MCI reset release the MCU MBOX is locked for MCU. The MCU shall set the DLEN to the size of the SRAM and release the MBOX, causing the SRAM to be zeroed. This is done to prevent data leaking between warm resets via the SRAM.
MCU Mailbox Target User
A Target user is an additional user that can access and process the MBOX request. This user can only be setup by MCU and only valid when the TARGET_USER_VALID bit is set.
One example of when a Target user becomes necessary is when the SOC wants Caliptra to process a MBOX request. The SOC will obtain a lock, MCU will see the command request is for Caliptra, MCU will add Caliptra as the Target user and notify Caliptra.
Another example is when MCU itself obtains the mailbox lock. It will add a Target user and notify the Target user via AXI or some other mechanism.
A Target user has read access to the entire mailbox and write access to:
- DLEN register
- TARGET_STATUS register
- TARGET_DONE register
- Mailbox SRAM
The Target user will notify MCU it is done processing by setting TARGET_STATUS and TARGET_DONE. Setting TARGET_DONE will interrupt MCU. If required, MCU will then update the CMD_STATUS register with the final status of the command for the Requestor.
If a second Target user is required it is the MCU's responsibility to:
- Clear TARGET_STATUS
- Clear TARGET_DONE
- Set new TARGET_USER
Otherwise these registers are cleared when the mailbox lock is released.
Target users must be an MCU Mailbox trusted user
MCU Mailbox Fully addressable SRAM
The SRAM is fully addressable and reads are not destructive in this mailbox.
Min Size: 0
Max Size: 2MB
If set to 0 the mailbox is not instantiated.
Accesses must be DWORD aligned and BYTE accesses are not supported by the SRAM.
MCU Mailbox SRAM Clearing
When the mailbox lock is released the SRAM is zeroed out from 0 to max DLEN set during the locking period. The flow for clearing the SRAM is:
- Requester releases lock
- MCU SRAM starts clearing
- MCU SRAM clearing ends
- Mailbox is unlocked
The Requester is locked out of the mailbox after step 1, even though the lock isn't cleared until step 4.
It is expected that agents write their content from 0 to DLEN. If an agent writes outside of this SRAM area, there is no security guarantee for that content because that data would not be zeroized between mailbox operations.
NOTE: When DLEN = 0 the entire MBOX SRAM is cleared.
MCU Mailbox Interrupts
The following interrupts are sent to MCU:
| Interrupt | Description |
|---|---|
| SOC request MBOX | Asserted when MCU has MBOX lock and an SOC agent tries to obtain the lock. MCU can decide to release the mailbox if this happens. |
| Mailbox data available from SOC | Asserted when a SOC agent gets lock and assert the EXECUTE register, indicating data is availalbe for MCU. |
| Target Done | Asserted when the Target user is done processing the data and is ready for MCU to consume or pass data to Requestor. |
The following interrup(s) are available for SOC consumption:
| Interrupt | Description |
|---|---|
| Mailbox data available from MCU | Asserted when MCU gets lock and assert the EXECUTE register, indicating data is available for SOC consumption. |
MCU Mailbox Errors
Each mailbox has the following errors:
| Error Type | Response | Description |
|---|---|---|
| Untrusted User Access | Read: Β - Data return 0 Β - AXI Error Write: Β - Data dropped Β - AXI Error | When an Untrusted user tries to access any address within the MBOX. |
| Trusted User Illegal Access | Read: Β - Data return 0 Write: Β - Data dropped | When a Trusted user tries to: - Access the mailbox when it doesn't have a lock - Tries to write to a register it doesn't have access to. - Tries to access an illegal SRAM address within the mailbox. |
| Single Bit ECC Error | - Interrupt to MCU - Mailbox ECC SB status set - Data corrected | Single bit ECC error while reading Mailbox. |
| Double Bit ECC Error | - Error interrupt to MCU - HW_NON_FATAL error set for SOC consumption - Mailbox ECC DB status set - Invalid data returned | Double bit ECC error while reading Mailbox. |
Whenever an agent reads data from the SRAM they either need to consume the Double Bit ECC interrupt wire or check the MCU Mailbox status registers to know if the data they received is valid.
MCU Mailbox MCU Access
When there is a mailbox lock the MCU has full access to the mailbox CSRs and SRAM in order to facilitage interactions and help with any lockup.
It is the only agent allowed to set TARGET_USER and update the final CMD_STATUS.
MCU Mailbox Address Map
| Start Address | End Address | Name | Description |
|---|---|---|---|
| 0x0000_0000 | 0x01F_FFFF* | MBOX SRAM | Mailbox SRAM |
| 0x0020_0000 | 0x020_003F | MBOX CSR | Mailbox Control Status Registers |
*NOTE: MBOX SRAM size is configurable, but MBOX always reserves 2MB address space. See MCU Mailbox Errors for how access to and invalid SRAM address are handled.
MCU SRAM

The MCU SRAM provides essential data and instruction memory for the Manufacturer Control Unit. This SRAM bank is utilized by the MCU to load firmware images, store application data structures, and create a runtime stack. The SRAM is accessible via the AXI interface and is mapped into the MCI's memory space for easy access and management. Exposing this SRAM via a restricted API through the SoC AXI interconnect enables seamless and secured Firmware Updates to be managed by Caliptra.
Min Size: 4KB
Max Size: 2MB
AXI USER filtering is used to restrict access within the MCU SRAM based on system state and accessor. Access permissions are based on the AXI USER input straps (either the MCU SRAM Config AXI_USER, or the MCU IFU/LSU AXI USERS). Any write attempt by an invalid AXI_USER is discarded and returns an error status. Any read attempt returns 0 data and an error status. The MCU SRAM contains two regions, a Protected Data Region and an Updatable Execution Region, each with a different set of access rules.
After each MCU reset the Updateable Execution Region may only be read/written by MCU SRAM Config User (typically Caliptra) prior to mcu_sram_fw_exec_region_lock input signal is set. Once fw_exec_region_lock is set it can be read/written by the MCU IFU or MCU LSU until MCU reset is asserted.
The Protected Data Region is only ever read/write accessible by MCU LSU. The span of each region is dynamically defined by the MCU ROM during boot up. Once MCU has switched to running Runtime Firmware, the RAM sizing shall be locked until any SoC-level reset. ROM uses the register FW_SRAM_EXEC_REGION_SIZE to configure the SRAM allocation in 4KB increments. FW_SRAM_EXEC_REGION_SIZE counts in base 0 meaning the smallest the Updateable Execution Region size can be is 4KB. It is possible for the entire SRAM to be allocated to the Updatable Execution Region and there be no Protected Data Region.
The entire MCU SRAM has ECC protection with no ability to disable. Single bit errors are detected and corrected. Double bit errors are detected and error.
MCI actions for single bit errors:
- Correct data and pass corrected data back to the initiator.
- Send maskable interrupt notification to MCU.
MCI actions for double bit errors:
- AXI SLVERR response to the initiator
- HW_ERROR_FATAL asserted and sent to SOC
MCU SRAM is accessible via DMI, see DMI MCU SRAM Access for more details.
MCU Hitless Update Handshake
The hitless flow is described in full in Caliptra Top Spec. The Caliptra SS Integration Spec contains all MCI warm reset and hitless update flows and restrictions. This section is focused on the HW registers both Caliptra and MCU will used to complete the flow.
MCI tracks three different RESET_REASON in its register bank:
| Reset type | Description |
|---|---|
WARM_RESET | MCI reset cycle. MCU SRAM has FW image but need Caliptra interaction before jumping to the FW image. |
FW_BOOT_UPD_RESET | First hitless update since MCI warm reset. MCU SRAM needs full initialization. |
FW_HITLESS_UPD_RESET | Second or greater hitless update since MCI warm reset. MCU SRAM can be partially initialized since valid content still exists in the MCU SRAM from previous firmware. |
WARM_RESET will be set by hardware when a warm reset occurs. It should be cleared by the Caliptra Core during a firmware boot or hitless update flow.
FW_BOOT_UPD_RESET and FW_HITLESS_UPD_RESET are typically set and cleared by the Caliptra Core during a firmware boot or hitless update flow. If Caliptra Core is not used in the design, the SoC needs to designate a different trusted entity to control these registers.
FW_EXEC_CTLR[2] is an input signal to the MCI and is sent as an interrupt (notif_cptra_mcu_reset_req_sts) to the MCU. This interrupt should be cleared by the MCU before it requests a reset with RESET_REQUEST.req. After a warm reset, setting FW_EXEC_CTRL[2] will trigger an interrupt to the MCU, indicating that the MCU should reset itself with RESET_REQUEST.req. After the first MCU reset request, when this input signal is cleared, it triggers the interrupt. The MCU is held in reset until FW_EXEC_CTRL[2] is set, with a minimum reset time determined by the MIN_MCU_RST_COUNTER MCI parameter.
MCI AXI Subordinate
MCI AXI Subordinate decodes the incoming AXI transaction and passes it onto the appropriate submodule within MCI.
The MCI AXI Sub will respond with an AXI error if one of the following conditions is met:
- Submodule error response
AXI access misses are:
- Writes dropped
- Reads return 0
MCI Interrupts

All interrupt status and control registers live in the CSR block. Each interrupt has the following properties:
- Status: W1C for SW to clear
- Enable: Prevents status from propagating. It does not block the status from being set.
- SW Trigger: Ability for SW to manually trigger the interrupt. Typically used for debug.
- Counter: Counts number of times the interrupt event was detected (SW or HW).
There are two different groups of interrupts
- Error
- Notification
Each group of interrupts has its own global status and enable registers that are an aggregate of all interrupts in the group. These status and enable registers have the same properties as the individual interrupt status and enable registers.
All interrupt groups are ORed and sent out on a signal mci_intr pin.
SW access to all interrupt registers are restricted to MCU.
MCI aggregates other Caliptra SS interrupts in this infrastructure. To clear these interrupts FW must:
- Service the interrupt at the IP source.
- Clear the interrupt in MCI.
Interrupts that are aggregated in MCI:
- Caliptra MBOX data available
- OTP FC Done Interrupt
MCI Error handling
MCI aggregates the error information (Fatal, Non-Fatal errors from Caliptra, any error signals that fuse controller, i3c etc.) and provides subsystem level FATAL and NON FATAL error signals. For all the error information being collected from other subystem modules, MCI also provides masking capability for MCU FW to program/enable based on SOC specific architectures to provide maximum flexibility.

Aggregate error register assignments are documented in the register specification: TODO: Add a link to rdl -> html file
Regions of 6 bits in the aggregate error registers are reserved for each component. MCU and Caliptra errors are connected to appropriate severity levels. Lifecycle controller, fuse controller and I3C are connected to both severities. Masks are used to set the severity of each error for these components. These can be configured by integrators, ROM, or runtime firmware.
| Error Register Bits | Component | Default Error Severity | Description |
|---|---|---|---|
| Aggregate error[5:0] | Caliptra core | Both | Caliptra errors |
| Aggregate error[11:6] | MCU | Both | DCCM double bit ECC error is fatal DCCM single bit ECC error is non-fatal |
| Aggregate error[17:12] | Life cycle controller | Fatal | LCC alerts [14:12] |
| Aggregate error[23:18] | OTP Fuse controller | Fatal | FC Error [23], FC alerts [22:18] |
| Aggregate error[29:24] | I3C | Non-Fatal | Peripheral reset [25] and escalated reset [24] pins from I3C |
| Aggregate error[31:30] | Spare bits | None | Spare bits for integrator use |
MCI also generates error signals for its own internal blocks, specifically for MCU SRAM & mailboxes double bit ECC and WDT.

MCI Fuse Storage Support
MCI also provides capability to store fuses required for Caliptra subsystem for Caliptra core's usage for production debug unlock feature. MCU will read the fuse controller for the production debug unlock hashes , write to the corresponding registers in the MCI block and lock the registers from being changed by MCU RT FW. Lock is done by writing to do a FUSE_WR_DONE (FIXME: Add specific register & bit names).
MCU Timer
Standard RISC-V timer interrupts for MCU are implemented using the mtime and mtimecmp registers defined in the RISC-V Privileged Architecture Specification. Both mtime and mtimecmp are included in the MCI register bank, and are accessible by the MCU to facilitate precise timing tasks. Frequency for the timers is configured by the SoC using the dedicated timer configuration register, which satisfies the requirement prescribed in the RISC-V specification for such a mechanism. These timer registers drive the timer_int pin into the MCU.
MCU Trace Buffer

MCI hosts the MCU trace buffer. It can hold up to 64 trace packets from the MCU, see MCU Trace Buffer Packet for the data format. Read access to the trace buffer is controlled by the LCC state Translator. When Debug Locked all AXI and DMI accesses to the trace buffer are rejected. See MCU Trace Buffer Error Handling for expected response while Debug Locked.
MCU RISC-V processor can enable/disable tracing with an internal CSR, by default it is enabled. Within MCI there is no way to disable traces.
The trace buffer is a circular buffer where the oldest data is overwritten by new traces when the buffer is full. When a trace packet is stored the write pointer increments by MCU Trace Buffer Packet Size/DWORD
The trace buffer is reset when MCI reset is asserted (warm reset).

MCU Trace Buffer SW Interface
Below is the SW interface to extract trace data:
| Register Name | Access Type | Description |
|---|---|---|
| DATA | RO | Trace data at READ_PTR location |
| READ_PTR | RW | Read pointer in trace buffer for DATA. NOTE: this is not an address, meaning increment by 1 to get the next entry. |
| WRITE_PTR | RO | Offset to store next trace entry in trace buffer. If VALID_DATA is set, WRITE_PTR - 1 is the newest trace entry. |
| STATUS.VALID_DATA | RO | Indicates at least one entry is valid in the trace buffer. |
| STATUS.WRAPPED | RO | Indicates the trace buffer has wrapped at least once. Meaning all entries in the trace buffer are valid. If 0, then the oldest entry in the buffer is at ptr=0. If 1, the oldest entry is at WRITE_PTR |
| CONFIG.TRACE_BUFFER_DEPTH | RO | Indicates the total number of 32 bit entries in the trace buffer. TRACE_BUFFER_DEPTH - 1 is the last valid WRITE/READ_PTR entry in the trace buffer. NOTE: This is the trace buffer depth and not the number of MCU Trace Buffer Packets. |
MCU Trace Buffer Registers Spec
MCU Trace Buffer Packet
A single MCU trace packet is more than 32 bits, meaning each trace takes up more than one READ/WRITE_PTR entry. Trace data is stored in the following format:

Assuming there is only one trace stored in the trace buffer the WRITE_PTR would read as 0x4. To get the entire trace packet the user would need to read offsets 0x0, 0x1, 0x2, and 0x3.
MCU Trace Buffer Extraction
To extract trace buffer data the user should send the following transaction:
- Write READ_PTR
- Read DATA
Repeat these steps until all data required has been extracted.
The user should use the combination of WRITE_PTR, VALID_DATA, WRAPPED, and TRACE_BUFFER_DEPTH to know where valid data lives in the trace buffer.
MCU Trace Buffer Error Handling
AXI access to the MCU Trace Buffer while not in debug mode will result in an AXI error.
DMI access to the MCU Trace Buffer while not in debug mode will result in writes being dropped and reads returning 0.
If the user sets the READ_PTR > (TRACE_BUFFER_DEPTH - 1) and tries to read the DATA register, the behavior is undefined.
MCI Debug
MCI Debug Access
MCI provides DMI access via MCU TAP and a DEBUG AXI USER address for debug access to MCI.
MCI DMI
NOTE: DMI access to MBOX0/1 is not enabled in Caliptra 2.0. This will be enabled in future Caliptra versions

The DMI port on MCU is a dedicated interface that is controlled via the MCU TAP interface. MCI provides two services when it comes to DMI:
-
MCU DMI enable control (uncore and core)
-
DMI interface for debug access to MCI address space
MCU DMI Enable Control
The MCU has two different DMI enables:
- Core
- Access to internal MCU registers
- Uncore
- Access to MCI registers
MCI provides the logic for these enables. When the following condition(s) are met the enables are set:
MCU Core Enable: Debug Mode
MCU Uncore Enable: Debug Mode OR LCC Manufacturing Mode OR DEBUG_INTENT strap set
Note: These are the exact same controls Calipitra Core uses for DMI enable
MCI DMI Interface
The MCI DMI Interface gives select access to the blocks inside MCI.
Access to MCI's DMI space (MCU Uncore) is split into two different levels of security:
| Access | Description | | :--------- | :--------- | | Debug Locked| Registers and memories behind this security level are always accessible over DMI whenever MCU uncore DMI enabled.| | Debug Unlock| Registers and memories behind this security level are only accessible over DMI when LCC is Debug Unlocked.|
Illegal accesses will result in writes being dropped and reads returning 0.
NOTE: MCI DMI address space is different than MCI AXI address space.
MCI DMI Memory Map
| Register Name | DMI Address | Access Type | Debug Locked Access | Debug Unlock Access |
|---|---|---|---|---|
| NOT ENABLED IN 2.0 MBOX0_DLEN | 0x50 | RO | Yes | |
| NOT ENABLED IN 2.0 MBOX0_DOUT | 0x51 | RO | Yes | |
| NOT ENABLED IN 2.0 MBOX0_STATUS | 0x52 | RO | Yes | |
| NOT ENABLED IN 2.0 MBOX0_DIN | 0x53 | WO | Yes | |
| NOT ENABLED IN 2.0 MBOX1_DLEN | 0x54 | RO | Yes | |
| NOT ENABLED IN 2.0 MBOX1_DOUT | 0x55 | RO | Yes | |
| NOT ENABLED IN 2.0 MBOX1_STATUS | 0x56 | RO | Yes | |
| NOT ENABLED IN 2.0 MBOX1_DIN | 0x57 | WO | Yes | |
| MCU_SRAM_ADDR | 0x58 | RW | Yes | |
| MCU_SRAM_DATA | 0x59 | RW | Yes | |
| MCU_TRACE_STATUS | 0x5A | RO | Yes | |
| MCU_TRACE_CONFIG | 0x5B | RO | Yes | |
| MCU_TRACE_WR_PTR | 0x5C | RO | Yes | |
| MCU_TRACE_RD_PTR | 0x5D | RW | Yes | |
| MCU_TRACE_DATA | 0x5E | RO | Yes | |
| HW_FLOW_STATUS | 0x5F | RO | Yes | |
| RESET_REASON | 0x60 | RO | Yes | |
| RESET_STATUS | 0x61 | RO | Yes | |
| FW_FLOW_STATUS | 0x62 | RO | Yes | |
| HW_ERROR_FATAL | 0x63 | RO | Yes | |
| AGG_ERROR_FATAL | 0x64 | RO | Yes | |
| HW_ERROR_NON_FATAL | 0x65 | RO | Yes | |
| AGG_ERROR_NON_FATAL | 0x66 | RO | Yes | |
| FW_ERROR_FATAL | 0x67 | RO | Yes | |
| FW_ERROR_NON_FATAL | 0x68 | RO | Yes | |
| HW_ERROR_ENC | 0x69 | RO | Yes | |
| FW_ERROR_ENC | 0x6A | RO | Yes | |
| FW_EXTENDED_ERROR_INFO_0 | 0x6B | RO | Yes | |
| FW_EXTENDED_ERROR_INFO_1 | 0x6C | RO | Yes | |
| FW_EXTENDED_ERROR_INFO_2 | 0x6D | RO | Yes | |
| FW_EXTENDED_ERROR_INFO_3 | 0x6E | RO | Yes | |
| FW_EXTENDED_ERROR_INFO_4 | 0x6F | RO | Yes | |
| FW_EXTENDED_ERROR_INFO_5 | 0x70 | RO | Yes | |
| FW_EXTENDED_ERROR_INFO_6 | 0x71 | RO | Yes | |
| FW_EXTENDED_ERROR_INFO_7 | 0x72 | RO | Yes | |
| RESET_REQUEST | 0x73 | RW | Yes | |
| MCI_BOOTFSM_GO | 0x74 | RW | Yes | |
| CPTRA_BOOT_GO | 0x75 | RW | Yes | |
| FW_SRAM_EXEC_REGION_SIZE | 0x76 | RW | Yes | |
| MCU_RESET_VECTOR | 0x77 | RW | Yes | |
| SS_DEBUG_INTENT | 0x78 | RW | Yes | |
| SS_CONFIG_DONE | 0x79 | RW | Yes | |
| SS_CONFIG_DONE_STICKY | 0x7A | RW | Yes | |
| MCU_NMI_VECTOR | 0x7B | RW | Yes | |
| MCI_DMI_MCI_HW_OVERRIDE (DMI ONLY Reg) | 0x7C | RW | Yes |
DMI Only Registers
MCI_DMI_MCU_HW_OVERRIDE
| Field Name | Bits | Access Type | Description |
|---|---|---|---|
mcu_sram_fw_exec_region_lock | [0] | RW | mcu_sram_fw_exec_region_lock control. ORed with input signal giving debugger control if Caliptra Core in reset while attempting MCU reset flow. |
reserved | [31:1] | RW | Reserved |
DMI MCU SRAM Access
MCU SRAM is only accessable via DMI while in Debug Unlock. While in Debug Unlock DMI has RW permission to the entire MCU SRAM.
Due to limited DMI address space the MCU SRAM is accessable via a two register mailbox.
MCU_SRAM_ADDRMCU_SRAM_DATA
The first MCU SRAM address is at address 0x0. The address is byte addressable, but all accesses must be DWORD aligned. Failure to align the address will result in undefined behavior. Access to addresses beyond the MCU SRAM size will result in address wrapping.
To write content to the MCU SRAM the flow is:
- Write
MCU_SRAM_ADDR - Write
MCU_SRAM_DATA
To read content from the MCU SRAM the flow is:
- Write
MCU_SRAM_ADDR - Read
MCU_SRAM_DATA
There is no error response on the DMI port, so any ECC error must be checked via the ECC registers in the MCI Register Bank.
Important: MCU core must be halted to access MCU SRAM via DMI. Failure to do so will result in collisions between the two interfaces and an error will be reported.
DMI MCU Trace Buffer Access
Access to the MCU Trace buffer via DMI is the same SW interface as specified in MCU Trace Buffer.
Access is limited to Debug Unlock mode only. Access to this space while not in Debug Unlock will result in writes being dropped and reads return 0x0.
MCI Boot FSM Breakpoint
The MCI Breakpoint is used as a stopping point for debugging Caliptra SS. At this breakpoint the user can either use one of the MCI Debug Access mechanisms to configure MCI before bringing MCU or Caliptra out of reset.
MCI Boot FSM Breakpoint Flow
- Assert mci_boot_seq_brkpoint
- Deassert MCI Reset
- DEBUG ACCESS TO MCI
- Set MCI's BRKPOINT_GO register to 1
- Through one of the MCI Debug Access methods.
- FSM will continue
MCI Design for Test (DFT)
Reset Controls
MCI controls various resets for other IPs like MCU and Caliptra Core. When the scan_mode input port is set these resets are directly controlled by the mcu_rst_b input intead of the internal MCI logic.
2041523
Caliptra Subsystem Integration Specification
Version 2p1
- Scope
- Overview
- Caliptra Subsystem High level diagram
- Integration Considerations
- Verification View of Caliptra Subsystem
- Caliptra Subsystem Top
- Caliptra Core
- MCU (Manufacturer Control Unit)
- Fuse Controller
- Overview
- Parameters & Defines
- Interface
- Fuse Macro Memory Map and Fuse Controller CSR Address Map
- FC Integration Requirements
- Direct Access Interface
- Initialization
- Programming interface
- Readout Sequence
- Sequences: Reset, Boot
- UDS & Field Entropy FIPS Zeroization Sequence
- FIPS Zeroization Sequence For ECC
- Miscellanious Fuse Integration Guidelines
- How to test : Smoke & more
- Generating the Fuse Partitions
- Fuse Controller Macro
- Life Cycle Controller
- MCI
- Overview
- Parameters & Defines
- Interface
- Memory Map / Address map
- MCI Integration Requirements
- Error Aggregation Connectivity Requirements
- Subsystem Internal Fuse Controller Initialization Connectivity Requirements
- Subsystem Internal Life Cycle Controller Initialization Connectivity Requirements
- MCI MCU Connectivity Requirements
- MCI Caliptra Core Connectivity Requirements
- LCC Gasket Connectivity Requirements
- MCU SRAM Sizing Requirements
- MCU MBOX SRAM Sizing Requirements
- Programming interface
- Sequences : Reset, Boot,
- Other requirements
- I3C core
- CDC analysis and constraints
- Reset Domain Crossing
- Synthesis
- Terminology
Scope
For Caliptra Subsystem, this document serves as a hardware integration specification. The scope of this document is to assist integrators in the integration of Caliptra Subsystem. It is not intended to serve as a hardware specification or to include micro-architectural details. This document includes Caliptra Subsystem top-level details along with parameters, defines, interfaces, memory map, programming reference, and guidelines on how to test the integration of the design.
Document Version
| Date | Document Version | Description |
|---|---|---|
| Jan 31st, 2025 | v0p8 | Work in progress |
| Apr 30th, 2025 | v1p0-rc1 | Initial release candidate of Caliptra Gen 2.0 Subsystem Documents. Specifcations updated with: - Detail on usage of all Subsystem flows such as Streaming Boot, Mailbox operation, and Debug Unlock - Details on design connectivity with top-level ports - Requirements and recommendations for integrators when adding Caliptra Subsystem to SoC designs |
| Oct 12th, 2025 | v2p1 | Final release of Caliptra Subsystem 2.1 |
Related repositories & specifications
The components described in this document are either obtained from open-source GitHub repositories, developed from scratch, or modified versions of open-source implementations. Links to relevant documentation and GitHub sources are shared in the following table.
Table 1: Related specification and repositories
| IP/Block | Code (GitHub URL) | Documentation (URL) |
|---|
| Caliptra | GitHub - chipsalliance/Caliptra| Caliptra Gen 2.0 Specification | Caliptra-SS | GitHub - chipsalliance/caliptra-ss| Hardware Specification Document | Caliptra-rtl | GitHub - chipsalliance/caliptra-rtl | Caliptra RTL documentation | | Cores-VeeR | GitHub - chipsalliance/Cores-VeeR-EL2 | VeeR EL2 Programmerβs Reference Manual | | I3C-Core | GitHub - chipsalliance/i3c-core | I3C core documentation | | Adams Bridge | GitHub - chipsalliance/adams-bridge | Adams Bridge Documentation |
Overview
The Caliptra Subsystem is designed to provide a robust Root of Trust (RoT) for datacenter-class System on Chips (SoCs), including CPUs, GPUs, DPUs, and TPUs. It integrates both hardware and firmware components to deliver essential security services such as identity establishment, measured boot, and attestation. By incorporating the Caliptra Subsystem, integrators can enhance the security capabilities of their SoCs, providing a reliable RoT that meets industry standards and addresses the evolving security needs of datacenter environments.
Caliptra Subsystem High level diagram
The following diagram provides a high-level overview of the Caliptra subsystem. It illustrates the key components and their interconnections within the system. For an in-depth understanding of the Caliptra Subystem refer to Caliptra Subsystem Hardware Specification Document.
Following high-level diagram helps integrators understand the overall architecture and the relationships between different components within the Caliptra subsystem.

Caliptra Subsystem includes:
- Caliptra Core: The Caliptra Core IP. For more information, see Caliptra: A Datacenter System on a Chip (SoC) Root of Trust (RoT).
- MCU (Manufacturer Control Unit): A microcontroller unit that manages various control tasks within the subsystem.
- I3C Core: An interface for connecting and communicating with I3C devices, which are used for providing streaming boot support and communicating with other I3C host controllers.
- Life Cycle Controller: A component that manages the different stages of the subsystem's lifecycle, including initialization, operation, and shutdown.
- Fuse Controller (OTP): A one-time programmable memory controller used for storing critical configuration data and security keys.
- MCI (Manufacturer Control Interface (for MCU)): Manages the communication between the processor and the memory components.
- Memories: Various memory components used for storing data and instructions required by the subsystem.
Integration Considerations
By performing these design and verification tasks, the integrator ensures that the Caliptra Subsystem is properly integrated and can function as intended within the larger system. Several files contain code that may be specific to an integrator's implementation and should be overridden. This overridable code is either configuration parameters with integrator-specific values or modules that implement process-specific functionality. Code in these files should be modified or replaced by integrators using components from the cell library of their fabrication vendor. The following table describes recommended modifications for each file.
| File | Description |
|---|---|
| css_mcu0_dmi_jtag_to_core_sync.v | Replace with a technology-specific sync cell. This synchronizer implements edge detection logic using a delayed flip flop on the output domain to produce a pulse output. Integrators must take care to ensure logical equivalence when replacing this logic with custom cells. |
| css_mcu0_beh_lib.sv | Replace css_mcu0_rvclkhdr/css_mcu0_rvoclkhdr with a technology-specific clock gater. Modifying this file may not be necessary if integrators override the clock gate module that is used by setting TECH_SPECIFIC_EC_RV_ICG. |
| css_mcu0_beh_lib.sv | Replace css_mcu0_rvsyncss (and css_mcu0_rvsyncss_fpga if the design will be implemented on an FPGA) with a technology-specific sync cell. |
| src/integration/rtl/caliptra_ss_includes.svh | Modify the parameter CPTRA_SS_ROM_SIZE_KB to define the correct size of the MCU ROM in integrated design. No other parameters in this file are permitted to be modified. |
Caliptra Core RTL modifications
It is mandatory that any build processes used (e.g. simulation, lint, synthesis) define the Verilog macro CALIPTRA_MODE_SUBSYSTEM, as described in the Caliptra Core Integration Specification. This ensures that Caliptra provides all Subsystem-related features and configuration. Example build scripts provided in the Caliptra Subsystem repository (such as Makefile) demonstrate how this might be performed.
Design Considerations
- Replace the AXI Interconnect: The subsystem utilizes an AXI-based interconnect to facilitate communication between components, with the Caliptra core connecting via an AXI interface. The integrator must replace the default AXI interconnect component with their proprietary interface. This ensures that the subsystem can communicate effectively with the rest of the subsystem components using the integrator's specific interconnect architecture.
- Connect the Memories: The integrator must connect the various memory components required by the subsystem. These memory components are used for storing data and instructions necessary for the operation of the Caliptra subsystem.
- No Changes to Internals: Integrators are not expected to make any changes to the internals of the design. The focus should be on ensuring proper integration and connectivity of the subsystem components.
Verification Considerations
- Connect the I3C Core GPIO with I3C host driver: The integrator must connect the I3C core (two target I3C devices) to the appropriate driver for the GPIO pins. This connection is crucial for enabling communication with I3C devices, which are used for communication within the subsystem.
Verification View of Caliptra Subsystem
The following block diagram shows details on verification view of Caliptra Subsystem

Memory Requirements
Integrators must instantiate SRAM components outside of the Caliptra Subsystem boundary (see SRAM Implementation for more details). SRAM wrapper logic within the Caliptra Subsystem boundary facilitates the connection to SRAM instances through memory export interfaces in the top-level port list. Integrators must connect the following memory export interfaces to the instantiated SRAM components.
SoC Specific in below table means the size of the memory is not fixed and can be configured based on the design requirements by integrator. The actual size will depend on the specific implementation and configuration of the Caliptra Subsystem.
| Device | Memory Name | Interface | Size | Access Type | Description |
|---|---|---|---|---|---|
| MCU0 | Instruction ROM | mcu_rom_mem_export_if | SoC Specific | Read-Only | Stores the instructions for MCU0 execution. Write-enable (we) and write-data (wdata) signals must be left unconnected, as MCU ROM has no write support. |
| MCU0 | Memory Export | cptra_ss_mcu0_el2_mem_export | SoC Specific | Read/Write | Memory export for MCU0 access |
| MCU0 | Shared Memory (SRAM) | cptra_ss_mci_mcu_sram_req_if | SoC Specific | Read/Write | Shared memory between MCI and MCU for data storage |
| MAILBOX | MBOX0 Memory | cptra_ss_mci_mbox0_sram_req_if | SoC Specific | Read/Write | Memory for MBOX0 communication |
| MAILBOX | MBOX1 Memory | cptra_ss_mci_mbox1_sram_req_if | SoC Specific | Read/Write | Memory for MBOX1 communication |
| Caliptra Core | ICCM, DCCM | cptra_ss_cptra_core_el2_mem_export | Refer to Caliptra Core spec | Read/Write | Interface for the Instruction and Data Closely Coupled Memory (ICCM, DCCM) of the core |
| Caliptra Core | Caliptra ROM | cptra_ss_cptra_core_imem | Refer to Caliptra Core spec | Read-Only | Interface for Caliptra ROM |
| Caliptra Core | Caliptra Mailbox SRAM | cptra_ss_cptra_core_mbox_sram | Refer to Caliptra Core spec | Read/Write | Interface for Caliptra mailbox memory |
| Caliptra Core | Caliptra MLDSA SRAM | mldsa_memory_export_req | Refer to Caliptra Core spec | Read/Write | Interface for SRAM instantiated within Adams Bridge block |
Caliptra Subsystem Top
The integration of the Caliptra Subsystem begins with the instantiation of the top-level RTL module, caliptra_ss_top.sv. This module serves as the primary entry point for the subsystem and encapsulates all the logic and components required for the functionality of the Caliptra Root of Trust (RoT). All signals must be connected based on the detailed interface and signal descriptions provided in this document. Ensure adherence to the signal direction, width, and functionality to guarantee proper integration with the host SoC.
Parameters & Defines
File at this path in the repository includes parameters and defines for Caliptra Subsystem src/integration/rtl/caliptra_ss_includes.svh
Interfaces & Signals
IMPORTANT NOTE: All signals assumed to by synchronous to cptra_ss_clk_i.
Table: Caliptra SS Straps
| Facing | Type | width | Name | Description |
|---|---|---|---|---|
| External | input | 32 | cptra_ss_strap_mcu_lsu_axi_user_i | MCU LSU AXI user strap input |
| External | input | 32 | cptra_ss_strap_mcu_ifu_axi_user_i | MCU IFU AXI user strap input |
| External | input | 32 | cptra_ss_strap_mcu_sram_config_axi_user_i | MCU SRAM Configuration AXI user strap input. |
| External | input | 32 | cptra_ss_strap_mci_soc_config_axi_user_i | MCI SOC Configuration AXI user strap input |
| External | input | 32 | cptra_ss_strap_caliptra_dma_axi_user_i | Caliptra DMA AXI user strap input |
| External | input | 32 | cptra_ss_strap_mcu_reset_vector_i | MCU reset vector strap input |
| External | input | 64 | cptra_ss_strap_caliptra_base_addr_i | Caliptra base address strap input |
| External | input | 64 | cptra_ss_strap_mci_base_addr_i | MCI base address strap input |
| External | input | 64 | cptra_ss_strap_recovery_ifc_base_addr_i | Recovery interface base address strap input |
| External | input | 64 | cptra_ss_strap_external_staging_area_base_addr_i | External staging area base address input |
| External | input | 64 | cptra_ss_strap_otp_fc_base_addr_i | OTP FC base address strap input |
| External | input | 64 | cptra_ss_strap_uds_seed_base_addr_i | UDS seed base address strap input |
| External | input | 32 | cptra_ss_strap_prod_debug_unlock_auth_pk_hash_reg_bank_offset_i | Prod debug unlock auth PK hash reg bank offset input |
| External | input | 32 | cptra_ss_strap_num_of_prod_debug_unlock_auth_pk_hashes_i | Number of prod debug unlock auth PK hashes input |
| External | input | 32 | cptra_ss_strap_generic_0_i | Provides the Caliptra ROM with a 32-bit pointer that encodes the location of the fuse controller's status register and the bit position of the idle indicator. Upper 16 bits: Bit index of the IDLE_BIT_STATUS within SOC_OTP_CTRL_STATUS. Lower 16 bits: Offset address of SOC_OTP_CTRL_STATUS within the SOC_IFC_REG space, relative to SOC_OTP_CTRL_BASE_ADDR. |
| External | input | 32 | cptra_ss_strap_generic_1_i | Provides the Caliptra ROM with a 32-bit pointer to the fuse controllerβs command register (CMD), enabling ROM-level control or triggering of fuse operations. |
| External | input | 32 | cptra_ss_strap_generic_2_i | Generic strap input 2 |
| External | input | 32 | cptra_ss_strap_generic_3_i | Generic strap input 3 |
| External | input | 1 | cptra_ss_debug_intent_i | Physical presence bit required to initiate the debug unlock flow. For more details, refer to the Production Debug Unlock Flow and How does Caliptra Subsystem enable manufacturing debug mode?. For SOCs that choose to use these features, this port should be connected to a GPIO |
| External | input | 16 | cptra_ss_strap_key_release_key_size_i | OCP L.O.C.K. MEK byte size. Expected to be 0x40. |
| External | input | 64 | cptra_ss_strap_key_release_base_addr_i | OCP L.O.C.K. MEK release base address. |
| External | input | 1 | cptra_ss_strap_ocp_lock_en_i | OCP L.O.C.K. enable. Allows OCP L.O.C.K. in progress to be set enabling hardware features specific to OCP L.O.C.K. such as AES Keyvault write path, Keyvault filtering rules, and Key Release via AXI DMA. Must be driven with a constant value 0 or 1. |
| External | input | 64 | cptra_ss_strap_external_staging_area_base_addr_i | Base AXI address for the external staging area used by Caliptra Core FW to stage FW images due to reduced MBOX SRAM size. See Caliptra External Staging Area for more details. |
AXI Interface (axi_if)
| Signal | Width | Direction (mgr) | Direction (sub) |
|---|---|---|---|
araddr | AW | output | input |
arburst | $bits(axi_burst_e) | output | input |
arsize | 3 | output | input |
arlen | 8 | output | input |
aruser | UW | output | input |
arid | IW | output | input |
arlock | 1 | output | input |
arvalid | 1 | output | input |
arready | 1 | input | output |
rdata | DW | input | output |
rresp | $bits(axi_resp_e) | input | output |
rid | IW | input | output |
rlast | 1 | input | output |
rvalid | 1 | input | output |
rready | 1 | output | input |
awaddr | AW | output | input |
awburst | $bits(axi_burst_e) | output | input |
awsize | 3 | output | input |
awlen | 8 | output | input |
awuser | UW | output | input |
awid | IW | output | input |
awlock | 1 | output | input |
awvalid | 1 | output | input |
awready | 1 | input | output |
wdata | DW | output | input |
wstrb | DW/8 | output | input |
wvalid | 1 | output | input |
wready | 1 | input | output |
wlast | 1 | output | input |
bresp | $bits(axi_resp_e) | input | output |
bid | IW | input | output |
bvalid | 1 | input | output |
bready | 1 | output | input |
Caliptra Subsystem Top Interface & Signals
| Facing | Type | width | Signal or Interface Name | Description |
|---|---|---|---|---|
| External | input | 1 | cptra_ss_clk_i | Caliptra subsystem clock input |
| External | input | 1 | cptra_ss_pwrgood_i | Power good signal input |
| External | input | 1 | cptra_ss_rst_b_i | Reset signal input, active low |
| External | input | 1 | cptra_ss_mci_cptra_rst_b_i | Reset signal input for Caliptra Core, active low. See Caliptra Core Reset Control for more details |
| External | output | 1 | cptra_ss_mci_cptra_rst_b_o | Reset signal output from MCI for Caliptra Core, active low. See Caliptra Core Reset Control for more details |
| External | input | 1 | cptra_ss_mcu_rst_b_i | Reset signal input for MCU, active low. See MCU Reset Control for more details |
| External | output | 1 | cptra_ss_mcu_rst_b_o | Reset signal output for MCU, active low. See MCU Reset Control for more details |
| External | output | 1 | cptra_ss_warm_reset_rdc_clk_dis_o | Clock disable for warm reset. Used to disable cptra_ss_rdc_clk_cg_o and cptra_ss_mcu_clk_cg_o clocks. Asserted a few clock cycles after cptra_ss_rst_b_i asserted and before cptra_ss_rst_b_o asserted. Deasserted a few clock cycles after cptra_ss_rst_b_i deasserted. |
| External | output | 1 | cptra_ss_early_warm_reset_warn_o | Early reset warn used to change security related signals to a safe value before reset is asserted. Needed since Caliptra Core clock gating is slightly after MCI clock gating/reset assertion. Example is the security_state fed from MCI to Caliptra Core. |
| External | output | 1 | cptra_ss_mcu_fw_update_rdc_clk_dis_o | Clock disable for MCU reset. Used to disable cptra_ss_mcu_clk_cg_o clock. Asserted a few clock cycles before cptra_ss_mcu_rst_b_o is asserted. Deasserted when MCI boot sequencer switches out of BOOT_RST_MCU state. |
| External | output | 1 | cptra_ss_rdc_clk_cg_o | Caliptra subsystem clock gated clock for RDC. Clock Control |
| External | output | 1 | cptra_ss_mcu_clk_cg_o | MCU clock gated clock for RDC. Clock Control |
| External | output | 1 | cptra_ss_rst_b_o | Caliptra subsystem reset aligned for RDC crossing Reset Control |
| External | axi_if | na | cptra_ss_cptra_core_s_axi_if_w_sub | Caliptra core AXI write sub-interface |
| External | axi_if | na | cptra_ss_cptra_core_s_axi_if_r_sub | Caliptra core AXI read sub-interface |
| External | axi_if | na | cptra_ss_cptra_core_m_axi_if_w_mgr | Caliptra core AXI write manager interface |
| External | axi_if | na | cptra_ss_cptra_core_m_axi_if_r_mgr | Caliptra core AXI read manager interface |
| External | axi_if | na | cptra_ss_mci_s_axi_if_w_sub | Caliptra Subsystem MCI AXI write sub-interface |
| External | axi_if | na | cptra_ss_mci_s_axi_if_r_sub | Caliptra Subsystem MCI AXI read sub-interface |
| External | axi_if | na | cptra_ss_mcu_rom_s_axi_if_w_sub | Caliptra Subsystem MCU ROM AXI write sub-interface. Writes to MCU ROM are not supported, this interface shall be left unconnected from AXI interconnect and tied to 0-value inputs. |
| External | axi_if | na | cptra_ss_mcu_rom_s_axi_if_r_sub | Caliptra Subsystem MCU ROM AXI read sub-interface |
| External | axi_if | na | cptra_ss_mcu_lsu_m_axi_if_w_mgr | Caliptra Subsystem MCU LSU AXI write manager interface |
| External | Output | 4 | cptra_ss_mcu_lsu_m_axi_if_awcache | Caliptra Subsystem MCU LSU AXI write manager address transaction attributes signal |
| External | Output | 3 | cptra_ss_mcu_lsu_m_axi_if_awprot | Caliptra Subsystem MCU LSU AXI write manager address protection type signal |
| External | Output | 4 | cptra_ss_mcu_lsu_m_axi_if_awregion | Caliptra Subsystem MCU LSU AXI write manager address region identifier signal |
| External | Output | 4 | cptra_ss_mcu_lsu_m_axi_if_awqos | Caliptra Subsystem MCU LSU AXI write manager address quality of service signal |
| External | axi_if | na | cptra_ss_mcu_lsu_m_axi_if_r_mgr | Caliptra Subsystem MCU LSU AXI read manager interface |
| External | Output | 4 | cptra_ss_mcu_lsu_m_axi_if_arcache | Caliptra Subsystem MCU LSU AXI read manager address transaction attributes signal |
| External | Output | 3 | cptra_ss_mcu_lsu_m_axi_if_arprot | Caliptra Subsystem MCU LSU AXI read manager address protection type signal |
| External | Output | 4 | cptra_ss_mcu_lsu_m_axi_if_arregion | Caliptra Subsystem MCU LSU AXI read manager address region identifier signal |
| External | Output | 4 | cptra_ss_mcu_lsu_m_axi_if_arqos | Caliptra Subsystem MCU LSU AXI read manager address quality of service signal |
| External | axi_if | na | cptra_ss_mcu_ifu_m_axi_if_w_mgr | Caliptra Subsystem MCU IFU AXI write manager interface |
| External | Output | 4 | cptra_ss_mcu_ifu_m_axi_if_awcache | Caliptra Subsystem MCU IFU AXI write manager address transaction attributes signal |
| External | Output | 3 | cptra_ss_mcu_ifu_m_axi_if_awprot | Caliptra Subsystem MCU IFU AXI write manager address protection type signal |
| External | Output | 4 | cptra_ss_mcu_ifu_m_axi_if_awregion | Caliptra Subsystem MCU IFU AXI write manager address region identifier signal |
| External | Output | 4 | cptra_ss_mcu_ifu_m_axi_if_awqos | Caliptra Subsystem MCU IFU AXI write manager address quality of service signal |
| External | axi_if | na | cptra_ss_mcu_ifu_m_axi_if_r_mgr | Caliptra Subsystem MCU IFU AXI read manager interface |
| External | Output | 4 | cptra_ss_mcu_ifu_m_axi_if_arcache | Caliptra Subsystem MCU IFU AXI read manager address transaction attributes signal |
| External | Output | 3 | cptra_ss_mcu_ifu_m_axi_if_arprot | Caliptra Subsystem MCU IFU AXI read manager address protection type signal |
| External | Output | 4 | cptra_ss_mcu_ifu_m_axi_if_arregion | Caliptra Subsystem MCU IFU AXI read manager address region identifier signal |
| External | Output | 4 | cptra_ss_mcu_ifu_m_axi_if_arqos | Caliptra Subsystem MCU IFU AXI read manager address quality of service signal |
| External | axi_if | na | cptra_ss_mcu_sb_m_axi_if_w_mgr | Caliptra Subsystem MCU System Bus AXI write manager interface |
| External | Output | 4 | cptra_ss_mcu_sb_m_axi_if_awcache | Caliptra Subsystem MCU System Bus AXI write manager address transaction attributes signal |
| External | Output | 3 | cptra_ss_mcu_sb_m_axi_if_awprot | Caliptra Subsystem MCU System Bus AXI write manager address protection type signal |
| External | Output | 4 | cptra_ss_mcu_sb_m_axi_if_awregion | Caliptra Subsystem MCU System Bus AXI write manager address region identifier signal |
| External | Output | 4 | cptra_ss_mcu_sb_m_axi_if_awqos | Caliptra Subsystem MCU System Bus AXI write manager address quality of service signal |
| External | axi_if | na | cptra_ss_mcu_sb_m_axi_if_r_mgr | Caliptra Subsystem MCU System Bus AXI read manager interface |
| External | Output | 4 | cptra_ss_mcu_sb_m_axi_if_arcache | Caliptra Subsystem MCU System Bus AXI read manager address transaction attributes signal |
| External | Output | 3 | cptra_ss_mcu_sb_m_axi_if_arprot | Caliptra Subsystem MCU System Bus AXI read manager address protection type signal |
| External | Output | 4 | cptra_ss_mcu_sb_m_axi_if_arregion | Caliptra Subsystem MCU System Bus AXI read manager address region identifier signal |
| External | Output | 4 | cptra_ss_mcu_sb_m_axi_if_arqos | Caliptra Subsystem MCU System Bus AXI read manager address quality of service signal |
| External | axi_if | na | cptra_ss_i3c_s_axi_if_w_sub | Caliptra Subsystem I3C AXI write sub-interface |
| External | axi_if | na | cptra_ss_i3c_s_axi_if_r_sub | Caliptra Subsystem I3C AXI read sub-interface |
| External | input | na | cptra_ss_lc_axi_wr_req_i | LC controller AXI write request input |
| External | output | na | cptra_ss_lc_axi_wr_rsp_o | LC controller AXI write response output |
| External | input | na | cptra_ss_lc_axi_rd_req_i | LC controller AXI read request input |
| External | output | na | cptra_ss_lc_axi_rd_rsp_o | LC controller AXI read response output |
| External | input | 128 | cptra_ss_raw_unlock_token_hashed_i | Hashed token for RAW unlock |
| External | input | na | cptra_ss_otp_core_axi_wr_req_i | OTP controller AXI write request input |
| External | output | na | cptra_ss_otp_core_axi_wr_rsp_o | OTP controller AXI write response output |
| External | input | na | cptra_ss_otp_core_axi_rd_req_i | OTP controller AXI read request input |
| External | output | na | cptra_ss_otp_core_axi_rd_rsp_o | OTP controller AXI read response output |
| External | input | 256 | cptra_ss_cptra_obf_key_i | Caliptra core obfuscation key input |
| External | input | CLP_CSR_HMAC_KEY_DWORDS | cptra_ss_cptra_csr_hmac_key_i | Caliptra core CSR HMAC key input |
| External | input | 1 | cptra_ss_cptra_core_jtag_tck_i | JTAG clock input |
| External | input | 1 | cptra_ss_cptra_core_jtag_tms_i | JTAG TMS input |
| External | input | 1 | cptra_ss_cptra_core_jtag_tdi_i | JTAG TDI input |
| External | input | 1 | cptra_ss_cptra_core_jtag_trst_n_i | JTAG reset input, active low |
| External | output | 1 | cptra_ss_cptra_core_jtag_tdo_o | JTAG TDO output |
| External | output | 1 | cptra_ss_cptra_core_jtag_tdoEn_o | JTAG TDO enable output |
| External | output | 125 | cptra_ss_cptra_generic_fw_exec_ctrl_o | Generic firmware execution control output |
| External | output | 1 | cptra_ss_cptra_generic_fw_exec_ctrl_2_mcu_o | Generic firmware execution control bit 2 from Caliptra output |
| External | input | 1 | cptra_ss_cptra_generic_fw_exec_ctrl_2_mcu_i | Generic firmware execution control bit 2 for MCU input |
| External | output | 1 | cptra_ss_all_error_fatal_o | Caliptra SS fatal error |
| External | output | 1 | cptra_ss_all_error_non_fatal_o | Caliptra SS non-fatal error |
| External | input | na | cptra_ss_lc_ctrl_jtag_i | LC controller JTAG request input |
| External | output | na | cptra_ss_lc_ctrl_jtag_o | LC controller JTAG response output |
| External | interface | na | cptra_ss_cptra_core_el2_mem_export | Caliptra core EL2 memory export interface |
| External | interface | na | mcu_rom_mem_export_if | MCU ROM memory export interface. Write-enable (we) and write-data (wdata) signals must be left unconnected, as MCU ROM has no write support. |
| External | output | 1 | cptra_ss_cptra_core_mbox_sram_cs_o | Mailbox SRAM chip select output |
| External | output | 1 | cptra_ss_cptra_core_mbox_sram_we_o | Mailbox SRAM write enable output |
| External | output | CPTRA_MBOX_ADDR_W | cptra_ss_cptra_core_mbox_sram_addr_o | Mailbox SRAM address output |
| External | output | CPTRA_MBOX_DATA_AND_ECC_W | cptra_ss_cptra_core_mbox_sram_wdata_o | Mailbox SRAM write data output |
| External | input | CPTRA_MBOX_DATA_AND_ECC_W | cptra_ss_cptra_core_mbox_sram_rdata_i | Mailbox SRAM read data input |
| External | output | 1 | cptra_ss_cptra_core_imem_cs_o | Instruction memory chip select output |
| External | output | CALIPTRA_IMEM_ADDR_WIDTH | cptra_ss_cptra_core_imem_addr_o | Instruction memory address output |
| External | input | CALIPTRA_IMEM_DATA_WIDTH | cptra_ss_cptra_core_imem_rdata_i | Instruction memory read data input |
| External | input | 1 | cptra_ss_cptra_core_bootfsm_bp_i | Boot FSM breakpoint input |
| External | output | 1 | cptra_ss_cptra_core_etrng_req_o | External TRNG request output |
| External | input | 4 | cptra_ss_cptra_core_itrng_data_i | Internal TRNG data input |
| External | input | 1 | cptra_ss_cptra_core_itrng_valid_i | Internal TRNG valid input |
| External | interface | na | cptra_ss_mci_mcu_sram_req_if | MCI MCU SRAM request interface |
| External | interface | na | cptra_ss_mci_mbox0_sram_req_if | MCI mailbox 0 SRAM request interface |
| External | interface | na | cptra_ss_mci_mbox1_sram_req_if | MCI mailbox 1 SRAM request interface |har
| External | output | 1 | cptra_ss_soc_mcu_mbox0_data_avail | MCU Mailbox0 data available output |
| External | output | 1 | cptra_ss_soc_mcu_mbox1_data_avail | MCU Mailbox1 data available output |
| External | interface | na | cptra_ss_mcu0_el2_mem_export | MCU0 EL2 memory export interface |
| External | input | 64 | cptra_ss_mci_generic_input_wires_i | Generic input wires for MCI |
| External | input | 1 | cptra_ss_mcu_no_rom_config_i | No ROM configuration input |
| External | input | 1 | cptra_ss_mci_boot_seq_brkpoint_i | MCI boot sequence breakpoint input |
| External | input | 1 | cptra_ss_lc_Allow_RMA_or_SCRAP_on_PPD_i | Allow RMA or SCRAP on PPD input |
| External | input | 1 | cptra_ss_FIPS_ZEROIZATION_PPD_i | Zeroization request with PPD input. If FIPS zeroization flow is required, it shall be set before Caliptra SS is out of reset. |
| External | input | 1 | cptra_ss_lc_sec_volatile_raw_unlock_en_i | Enables Volatile TEST_UNLOCKED0 state transition infra (see Volatile-Unlock) |
| External | output | 1 | cptra_ss_dbg_manuf_enable_o | Indication that the debug is unlocked for manufacturing state and this is set by Caliptra Core |
| External | output | 64 | cptra_ss_cptra_core_soc_prod_dbg_unlock_level_o | Indication that the debug is unlocked for production state. Each bit represents a debug level. Currently, 8-bit is supported with Caliptra ROM |
| External | output | na | caliptra_ss_life_cycle_steady_state_o | Life-cycle state broadcasted by fuse macro for any additional SOC specific use cases |
| External | output | 1 | caliptra_ss_otp_state_valid_o | One-bit valid indicator for the broadcast life-cycle state (caliptra_ss_life_cycle_steady_state_o). |
| External | output | 1 | caliptra_ss_volatile_raw_unlock_success_o | Asserted when the life-cycle controller grants the volatile-unlock state and remains asserted until the next power-cycle. This transition bypasses the fuse macro, so caliptra_ss_life_cycle_steady_state_o and caliptra_ss_otp_state_valid_o do not reflect it. |
| External | output | na | cptra_ss_lc_escalate_en_o | Life-cycle controller signal indicating that escalation is enabled at LCC and FC |
| External | output | na | cptra_ss_lc_check_byp_en_o | Life-cycle controller signal indicating that external clock is accepted |
| External | output | 64 | cptra_ss_mci_generic_output_wires_o | Generic output wires for MCI |
| External | input | 1 | cptra_ss_mcu_jtag_tck_i | MCU JTAG clock input |
| External | input | 1 | cptra_ss_mcu_jtag_tms_i | MCU JTAG TMS input |
| External | input | 1 | cptra_ss_mcu_jtag_tdi_i | MCU JTAG TDI input |
| External | input | 1 | cptra_ss_mcu_jtag_trst_n_i | MCU JTAG reset input, active low |
| External | output | 1 | cptra_ss_mcu_jtag_tdo_o | MCU JTAG TDO output |
| External | output | 1 | cptra_ss_mcu_jtag_tdoEn_o | MCU JTAG TDO enable output |
| External | input | 1 | cptra_ss_i3c_scl_i | I3C clock input |
| External | input | 1 | cptra_ss_i3c_sda_i | I3C data input |
| External | output | 1 | cptra_ss_i3c_scl_o | I3C clock output |
| External | output | 1 | cptra_ss_i3c_sda_o | I3C data output |
| External | output | 1 | cptra_ss_i3c_scl_oe | I3C clock output enable |
| External | output | 1 | cptra_ss_i3c_sda_oe | I3C data output enable |
| External | input | 1 | cptra_i3c_axi_user_id_filtering_enable_i | I3C AXI user filtering enable (active high) |
| External | output | 1 | cptra_ss_sel_od_pp_o | Select open-drain push-pull output |
| External | output | 1 | cptra_ss_i3c_recovery_payload_available_o | I3C indicates recovery payload is available. If there is no external I3C it should be looped back to cptra_ss_i3c_recovery_payload_available_i. If there is an external I3C it can be combined with or replaced with SOC logic and connected to cptra_ss_i3c_recovery_payload_available_i |
| External | input | 1 | cptra_ss_i3c_recovery_payload_available_i | I3C indication for Caliptra Core that a recovery payload is available. If no external I3C should be connected to cptra_ss_i3c_recovery_payload_available_o. If external I3C it can be connected to a combination of SOC logic + cptra_ss_i3c_recovery_payload_available_o |
| External | output | 1 | cptra_ss_i3c_recovery_image_activated_o | Indicates the recovery image is activated. If there is no external I3C it should be looped back to cptra_ss_i3c_recovery_image_activated_i. If there is an external I3C it can be combined with or replaced with SOC logic and connected to cptra_ss_i3c_recovery_image_activated_i |
| External | input | 1 | cptra_ss_i3c_recovery_image_activated_i | I3C indication for Caliptra Core that the recovery image is activated. If no external I3C should be connected to cptra_ss_i3c_recovery_image_activated_o. If there is an external I3C it can be connected to a combination of SOC logic + cptra_ss_i3c_recovery_image_activated_o |
| External | input | 64 | cptra_ss_cptra_core_generic_input_wires_i | Generic input wires for Caliptra core |
| External | input | 1 | cptra_ss_cptra_core_scan_mode_i | Caliptra core scan mode input |
| External | output | 1 | cptra_error_fatal | Fatal error output |
| External | output | 1 | cptra_error_non_fatal | Non-fatal error output |
| External | output | 1 | cptra_ss_mcu_halt_status_o | MCU halt status |
| External | output | 1 | cptra_ss_mcu_halt_status_i | MCU halt status input used by MCI |
| External | output | 1 | cptra_ss_mcu_halt_ack_o | MCU halt ack |
| External | output | 1 | cptra_ss_mcu_halt_ack_i | MCU halt ack input used by MCI |
| External | output | 1 | cptra_ss_mcu_halt_req_o | MCU halt request |
Integration Requirements
Clock
The cptra_ss_clk_i signal is the primary clock input for the Caliptra Subsystem.
- Signal Name
cptra_ss_clk_i - Required Frequency 333* MHz to 400 MHz
- I3C core imposes requirement for minimum operating clock frequency set to 333 MHz or higher to meet 12ns tSCO timing.
- 333 MHz was calculated assuming SCL PAD -> D and SDA Q -> PAD timing is 0. SOCs with large timing delays might need to run at a faster clock frequency to meet tSCO timing of 12ns.
- SoCs that run Caliptra lower than 333 MHz will limit the max I3C SCL frequency. See I3C Phy Spec for more details.
- This was changed from 170 MHz floor due to CDC issue found in I3C core:
- I3C core imposes requirement for minimum operating clock frequency set to 333 MHz or higher to meet 12ns tSCO timing.
- Clock Source Must be derived from the SoCβs clock generation module or a stable external oscillator.
- Integration Notes
- Verify that the SoC or system-level clock source provides a stable clock.
- The clock signal must be properly buffered if necessary to meet the subsystem's setup and hold timing requirements.
- If a different frequency is required, ensure that a clock divider or PLL is used to generate clock before connection.
The cptra_ss_rdc_clk_cg_o output clock is a clock gated version of cptra_ss_clk_i. It is clock gated whenever cptra_ss_rst_b is asserted to avoid RDC issues from the warm reset domain to the cold reset domain/memories.
- Signal Name
cptra_ss_rdc_clk_cg_o - Required Frequency Same as
cptra_ss_clk_i. - Clock Source Caliptra SS MCI clock gater
- Integration Notes
- Gated a few clock cycles before
cptra_ss_rst_b_oasserted and remains gated until reset is deasserted. - MCU SRAM and MCU MBOX memories shall be connected to this clock to avoid RDC issues.
- Clock gating controlled by
cptra_ss_warm_reset_rdc_clk_dis_o. - Any SOC logic on a deeper reset domain than CSS can use this clock to resolve RDC issues.
- Gated a few clock cycles before
The cptra_ss_mcu_clk_cg_o output clock is a gated version of cptra_ss_clk_i. It is gated whenever cptra_ss_mcu_rst_b_o is asserted to avoid RDC issues within the MCU warm and cold reset domains.
- Signal Name
cptra_ss_mcu_clk_cg_o - Required Frequency Same as
cptra_ss_clk_i. - Clock Source Caliptra SS MCI clock gater
- Integration Notes
- Gated a few clock cycles before
cptra_ss_mcu_rst_b_oasserted and remains gated until reset is deasserted. - Clock gating controlled by
cptra_ss_mcu_fw_update_rdc_clk_dis_oandcptra_ss_warm_reset_rdc_clk_dis_o. - Any SOC logic on a deeper reset domain than MCU can use this clock to resolve RDC issues.
- Gated a few clock cycles before
Reset
The cptra_ss_rst_b_i signal is the primary reset input for the Caliptra Subsystem. It must be asserted low to reset the subsystem and de-asserted high to release it from reset. Ensure that the reset is held low for a sufficient duration (minimum of 2 clock cycles) to allow all internal logic to initialize properly.
- Signal Name
cptra_ss_rst_b_i - Active Level Active-low (
0resets the subsystem,1releases reset) - Reset Type Synchronous with the
cptra_ss_clk_isignal - Integration Notes
- The reset signal must be synchronized to the
cptra_ss_clk_iclock to prevent metastability issues. - If the reset source is asynchronous, a synchronizer circuit must be used before connecting to the subsystem.
- During SoC initialization, assert this reset signal until all subsystem clocks and required power domains are stable.
- It is illegal to only toggle
cptra_ss_rst_b_iuntil both Caliptra and MCU have received at least one FW update. Failure to follow this requirement could cause them to execute out of an uninitialized SRAM. - SOC should assert
cptra_ss_reset_b_iaftercptra_ss_mcu_halt_status_ois asserted to guarantee MCU is idle. This will guarantee no outstanding AXI transactions from MCU and help avoid RDC issues.
- The reset signal must be synchronized to the
The cptra_ss_rst_b_o is a delayed version of cptra_ss_rst_b_i to ensure cptra_ss_rdc_clk_cg_o is gated before reset is asserted. This reset is needed for the purpose of RDC between the warm reset domain and the cold reset/memory domain.
- Signal Name
cptra_ss_rst_b_o - Active Level Active-low (
0resets the subsystem,1releases reset) - Reset Type Synchronous with the
cptra_ss_rdc_clk_cg_osignal - Integration Notes
- SOCs shall use this reset for any memory logic connected to MCU SRAM or MCU MBOX to avoid RDC corruption of the memories.
- It is recommended to be used for SOC AXI interconnect if it is on the same reset domain as Caliptra SS to avoid RDC issues.
- SOC logic on
cptra_ss_rst_b_idomain and transitions into a deeper reset domain can use this reset paired withcptra_ss_rdc_clk_cg_oto avoid RDC issues.
Power Good Signal
The cptra_ss_pwrgood_i signal serves as an indicator of stable power for the Caliptra Subsystem. When asserted (1), it confirms that power is stable and the subsystem can operate normally. When deasserted (0), the signal triggers a hard reset of the subsystem. Deassertion must be synchronized to cptra_ss_clk_i to avoid metastability issues.
- Signal Name
cptra_ss_pwrgood_i - Active Level Active-high (
1indicates stable power,0triggers reset) - Assertion Type Asynchronous
- Deassertion Type Synchronous to
cptra_ss_clk_i - Integration Notes
- Ensure
cptra_ss_pwrgood_iis properly generated by the power management unit or system power controller. - Since assertion is asynchronous, it must be immediately driven high once power is stable.
- Use a synchronizer to properly align deassertion with
cptra_ss_clk_ito prevent glitches. - If
cptra_ss_pwrgood_iremains low, the Caliptra Subsystem will remain in a hard reset state.
- Ensure
Connecting AXI Interconnect
Integrator must connect following list of manager and subordinates to axi interconnect.
- List of AXI Manager connections to AXI interconnect.
| Manager AXI If Name | Description |
|---|---|
cptra_ss_mcu_lsu_m_axi_if | Manager interface for MCU Load/Store Unit (LSU). All additional AXI signals present in the top-level port list that are not part of the axi_if interface must also be connected to the AXI interconnect (AxCACHE, AxPROT, AxREGION, AxQOS). |
cptra_ss_mcu_ifu_m_axi_if | Manager interface for MCU Instruction Fetch Unit (IFU). All additional AXI signals present in the top-level port list that are not part of the axi_if interface must also be connected to the AXI interconnect (AxCACHE, AxPROT, AxREGION, AxQOS). |
cptra_ss_mcu_sb_m_axi_if | Manager interface for MCU System Bus (SB). Used for debug only. All additional AXI signals present in the top-level port list that are not part of the axi_if interface must also be connected to the AXI interconnect (AxCACHE, AxPROT, AxREGION, AxQOS). |
cptra_ss_cptra_core_m_axi_if | Manager interface for the Caliptra Core AXI transactions. Additional signals are unused and may be tied to 0 at the interconnect (AxCACHE, AxPROT, AxREGION, AxQOS). |
-
AXI USER width is 32-bits for all AXI interfaces in the Caliptra Subsystem. Only the Address User signals are used (ARUSER and AWUSER) for secure access filtering. Other USER signals are either tied to 0 or not used (WUSER, RUSER, BUSER). ARUSER and AWUSER must be passed unmodified through the AXI interconnect to all AXI subordinates in the Subsystem. Each logic block inside the Subsystem is responsible for performing its own AXI User filtering based on access privileges. AXI interconnect is only responsible for passing the unmodified signals along with the transaction requests, not for performing any access filtering.
-
AXI ID width at each MCU manager interface must not be modified from the configured values. ID width for each of the MCU AXI Manager interfaces is defined by the <IF_NAME>_BUS_TAG parameter from this file: css_mcu0_el2_param.vh. Port connections may be seen in mcu_top.sv. ID Width of the Caliptra DMA AXI Manager interface is defined in soc_ifc_pkg.sv.
- IFU_BUS_TAG: 3. Interconnect should support ID values 0-7.
- LSU_BUS_TAG: 3. Interconnect should support ID values 0-7.
- SB_BUS_TAG: 1. Interconnect should support ID values 0,1.
- CPTRA_AXI_DMA_ID_WIDTH: 5. ID signals are tied to constant 0.
-
For each AXI subordinate interface in the Subsystem the following table shows ID WIDTH default value, file to review for the configured definition, and any support for configurability. | Interface | Default ID WIDTH | Reference File | Description | | ----------- | ------------------ | ---------------------------------- | ---------------------------------------------------------------- | | cptra_ss_cptra_core_s_axi_if | 8 | config_defines.svh | Default value of 8 is controlled using the macro
CALIPTRA_AXI_ID_WIDTH. | | cptra_ss_mcu_rom_s_axi_if | 8 | config_defines.svh | Default value of 8 is controlled using the macroCALIPTRA_AXI_ID_WIDTH. | | cptra_ss_mci_s_axi_if | 8 | caliptra_ss_top.sv | ID_WIDTH value is derived from the width of AxID signals in the connected AXI interface. | | cptra_ss_i3c_s_axi_if | 8 | i3c_defines.svh | Default value of 8 is controlled using the macroAXI_ID_WIDTH. | | cptra_ss_lc_axi_wr, cptra_ss_lc_axi_rd | 8 | src/tlul/rtl/tlul_pkg.sv | Default value is derived from the macroCALIPTRA_AXI_ID_WIDTH, overrideable using the macroCALIPTRA_SS_TLUL_AXI_ID_WIDTH. | | cptra_ss_otp_core_axi_wr, cptra_ss_otp_core_axi_rd | 8 | src/tlul/rtl/tlul_pkg.sv | Default value is derived from the macroCALIPTRA_AXI_ID_WIDTH, overrideable using the macroCALIPTRA_SS_TLUL_AXI_ID_WIDTH. | -
AXI subordinates in the Subsystem may accept up to 2 Read and 2 Write requests in total, but each request is serviced in order and responses are provided in order. Integrators may configure the interconnect to issue 1 or 2 outstanding transactions, but are recommended to configure only a single outstanding request at a time for area (buffer) optimizations in the interconnect and because there is no significant performance improvement by queueing multiple requests.
-
Subordinate Address Map requirements
- The MCU is configured with several internal address assignments that must not be used when assigning SOC addresses for AXI subordinates on the AXI interconnect. The following table shows these restricted regions:
| Start Address | End Address | Name | Description |
|---|---|---|---|
| 64'h5000_0000 | 64'h5FFF_FFFF | MCU DCCM | MCU Data Closely Coupled Memory. No external subordinates may be assigned address space in the same 256MiB region as the DCCM. For more details, refer to the VeeR EL2 Programmer's Reference Manual. |
| 64'h6000_0000 | 64'h6FFF_FFFF | MCU PIC | MCU Programmable Interrupt Controller. No external subordinates may be assigned address space in the same 256MiB region as the PIC. For more details, refer to the VeeR EL2 Programmer's Reference Manual. |
-
Subordinate Address Map (reference only) / List of sub connected to Interconnect
- The following address map is a suggested address map for subordinates for the subsystem design. It details the memory layout and the connections between different components within the Caliptra subsystem.
| Start Address | End Address | Address Width | Subordinate | Name | Description |
|---|---|---|---|---|---|
| 64'h1000_0000 | 64'h1FFF_FFFF | - | 0 | n/a | Reserved |
| 64'h2000_4000 | 64'h2000_4FFF | 12 | 1 | I3c | I3C Core |
| 64'h8000_0000 | 64'h80FF_FFFF | 24 | 2 | MCU ROM | MCU ROM |
| 64'hA002_0000 | 64'hA003_FFFF | 17 | 3 | SoC IFC | Caliptra Core AXI subordinate interface |
| 64'h2100_0000 | 64'h21DF_FFFF | 24 | 4 | MCI | Manufacturer Control Interface (for MCU) |
| 64'h7000_0000 | 64'h7000_01FF | 9 | 5 | Fuse Ctrl | Fuse Controller |
| 64'h7000_0400 | 64'h7000_05FF | 9 | 6 | Life Cycle Ctrl | Life Cycle Controller |
-
Following are the header files path for the below suggested address map. These files would be useful in defining the address map using the given RDL Files.
Caliptra Subsystem Reference Register Map
Caliptra Subsystem Reference Register Map. Please note, any addresses are for the example purpose only.
FW Execution Control Connections
FW Execute Control is typically controlled by Caliptra. This means cptra_ss_cptra_generic_fw_exec_ctrl_2_mcu_o should be looped back and directly connected to cptra_ss_cptra_generic_fw_exec_ctrl_2_mcu_i. If the SOC decided to not use Caliptra Core, the SOC must drive cptra_ss_cptra_generic_fw_exec_ctrl_2_mcu_i the same way Caliptra Core drives this signal.
- On same reset at MCI
- Synchronous to MCI clock domain
- 1 indicates FW patch is valid in the MCU SRAM
- 0 indicates FW patch is invalid and will request MCU to reset itself See Hitless Update Flow to understand exactly when this signal shall be set/cleared in the hitless FW flow.
Caliptra Core Reset Control
Typically Caliptra reset is directly controlled by MCI. This means cptra_ss_mci_cptra_rst_b_o is directly looped back to cptra_ss_mci_cptra_rst_b_i.
If an SOC wants to keep Caliptra in reset they can tie off cptra_ss_mci_cptra_rst_b_i and not user cptra_ss_mci_cptra_rst_b_o.
If an SOC wants to modify Caliptra reset they can do so by adding additional logic to the above signals.
NOTE: Caliptra SS RDC and CDC are only evaluated when the MCI control is looped back to Caliptra. Any modification to this reset control requires a full RDC and CDC analysis done by the SOC integration team.
MCU Reset Control
Typically MCU reset is directly controlled by MCI. This means cptra_ss_mcu_rst_b_o is directly looped back to cptra_ss_mcu_rst_b_i.
The SOC can choose to delay the MCU reset deassertion. The SOC should be aware that MCU clock enable is based off cptra_ss_mcu_rst_b_o.
If the SOC wants to delay assertion of MCU reset this can be done, but integrators need to be aware the MCU reset counter (MIN_MCU_RST_COUNTER_WIDTH) starts counting when cptra_ss_mcu_rst_b_i asserts. Meaning MCU could be in reset for shorter than expected. To resolve this issue the SOC should implement their own reset counter to delay the reset deassertion.
Arbitrary reset assertions/deassertions should not be done unless the integrator understands exactly what they are doing. This can cause RDC issues within Caliptra SS.
NOTE: Caliptra SS RDC and CDC are only evaluated when MCU reset is looped back. Any modification to this reset control requires a full RDC and CDC analysis done by the SOC integration team.
SRAM implementation
Overview
SRAMs are instantiated at the SoC level. Caliptra Subsystem provides the interface to export SRAMs from internal components. Components that export SRAMs include:
- Caliptra Core (see Caliptra Core integration specification)
- MCU (RISC-V core)
- MCI (Mailbox SRAM and MCU SRAM)
SRAM repair logic (for example, BIST) and its associated fuses, which is proprietary to companies and their methodologies, is implemented external to the Caliptra Subsystem boundary.
SRAMs must NOT go through BIST or repair flows across a βwarm resetβ. SoC shall perform SRAM repair during a powergood cycling event ("cold reset") and only prior to deasserting cptra_ss_rst_b_i. During powergood cycling events, SoC shall also initialize all entries in the SRAM to a 0 value prior to deasserting cptra_ss_rst_b_i. This requirement can not be completed by MCU (or any other components in Subsystem) because it is a function of the proprietary SRAM management logic.
MCU mailbox and executable SRAMs are implemented with ECC protection. Data width for the mailbox is 32-bits, with 7 parity bits for a Hamming-based SECDED (single-bit error correction and double-bit error detection).
RISC-V internal memory export
To support synthesis flexibility and ease memory integration to various fabrication processes, all SRAM blocks inside the RISC-V cores (Caliptra Core and MCU) are exported to an external location in the testbench. A single unified interface connects these memory blocks to their parent logic within the RISC-V core. Any memory implementation may be used to provide SRAM functionality in the external location in the testbench, provided the implementation adheres to the interface requirements connected to control logic inside the processor. Memories behind the interface are expected to be implemented as multiple banks of SRAM, from which the RISC-V processor selects the target using an enable vector. The I-Cache has multiple ways, each containing multiple banks of memory, and SOC SRAM implementations for I-Cache must be compatible with the exported interface.
The following memories are exported:
- Instruction Closely-Coupled Memory (ICCM) (Caliptra Core only)
- Data Closely Coupled Memory (DCCM) (Caliptra Core and MCU)
- Instruction Cache (I-Cache) (MCU only)
SRAM timing behavior
- [Writes] SRAM input wren signal is asserted simultaneously with input data and address. Input data is stored at the input address 1 clock cycle later.
- [Reads] SRAM input clock enable signal is asserted simultaneously with input address. Output data is available 1 clock cycle later from a flip-flop register stage.
The following figure shows the SRAM interface timing.
Figure: SRAM interface timing

SRAM parameterization
Parameterization for ICCM/DCCM/I-Cache memories is derived from the configuration of the VeeR RISC-V core that has been selected for Caliptra Subsystem integration. Parameters defined in the VeeR core determine signal dimensions at the Subsystem top-level interface and drive requirements for SRAM layout. For details about interface parameterization, see the Interfaces & Signals section.
Example SRAM machine check reliability integration
This section describes an example implementation of integrator machine check reliability.
This example is applicable to scenarios where an integrator may need control of or visibility into SRAM errors for purposes of reliability or functional safety. In such cases, integrators may introduce additional layers of error injection, detection, and correction logic surrounding SRAMs. The addition of such logic is transparent to the correct function of Caliptra Subsystem, and removes integrator dependency on Caliptra Subsystem components for error logging or injection.
Note that the example assumes that data and ECC codes are in non-deterministic bit-position in the exposed SRAM interface bus. Accordingly, redundant correction coding is shown in the integrator level logic (i.e., integrator_ecc(caliptra_data, caliptra_ecc)). If the Caliptra Subsystem data and ECC are deterministically separable at the Caliptra Subsystem interface, the integrator would have discretion to store the ECC codes directly and calculate integrator ECC codes for the data alone.
Figure: Example machine check reliability implementation

Error detection and logging
- Caliptra Subsystem IP shall interface to ECC protected memories.
- Caliptra Subsystem IP calculates and applies its own ECC code, which produces a total of 39-bit data written to external or INTEGRATOR instantiated SRAMs.
- Each 39-bit bank memory internally calculates 8-bit ECC on a write and stores 47 bits of data with ECC into SRAM.
- On read access syndrome is calculated based on 39-bit data.
- If parity error is detected and syndrome is valid, then the error is deemed single-bit and correctable.
- If no parity error is detected but syndrome == 0 or the syndrome is invalid, the error is deemed uncorrectable.
- On both single and double errors, the read data is modified before being returned to Caliptra Subsystem.
- Since single-bit errors shall be corrected through INTEGRATOR instantiated logic, Caliptra Subsystem never sees single-bit errors from SRAM.
- Double-bit or uncorrectable errors would cause unpredictable data to be returned to Caliptra Subsystem. Since this condition shall be detected and reported to MCRIP, there is no concern or expectation that Caliptra Subsystem will operate correctly after a double error.
- On detection, single errors are reported as transparent to MCRIP, double errors are reported as fatal.
- Along with error severity, MCRIP logs physical location of the error.
- After MCRIP logs an error, it has a choice to send out in-band notification to an external agent.
- MCRIP logs can be queried by SoC software.
Error injection
- MCRIP supports two error injection modes: intrusive and non-intrusive.
- Intrusive error injection:
- Can force a single or double error to be injected, which would result in incorrect data to be returned on read access.
- The intrusive error injection mode is disabled in Production fused parts via Security State signal.
- Non-intrusive error injection:
- Allows external software to write into MCRIP error log registers.
- The non-intrusive error injection does not interfere with the operation of memories.
- The non-intrusive error injection is functional in Production fused parts.
Caliptra Subsystem error handling flow
- Any implementation of error and recovery flows must adhere to the error handling requirements specified in Caliptra.md
- See MCI error handling for more details on MCI error infrastructure and error handling in Caliptra Subsystem.
- SoC level reporting and handling of fatal & non-fatal errors is product-specific architecture, outside the scope of Caliptra Subsystem definition. For example, a CPU and a PCIe device may handle fatal and non-fatal errors differently.
Programming interface
There are two primary programming avenues to interface with the Caliptra Subsystem:
-
MCU Firmware
- Description: This method involves programming the Microcontroller Unit (MCU) to execute firmware.
- Details: For more information on how to program the MCU and execute firmware via the MCU, please refer to the MCU Programming Interface documentation.
-
Caliptra Firmware
- Description: This method involves programming the Caliptra Core to execute firmware.
- Details: For more information on how to program and execute Caliptra Core firmware, please refer to the Caliptra Core References and Related Specifications.
Sequences
Reset Sequence:
- De-assert
cptra_ss_rst_b_iafter the primary clock (clk_i) stabilizes.
How to test
Reference tests are available at caliptra-ss\src\integration\test_suites
| Test Suite Name | Description |
|---|---|
MCU_HELLO_WORLD | Runs a basic "Hello World" program on the MCU to verify basic operation. |
MCU_CPTRA_BRINGUP | Tests the bring-up sequence of the MCU in the Caliptra Subsystem. |
MCU_DCCM_ACCESS | Validates access to the Data Closely Coupled Memory (DCCM) by the MCU. |
MCU_FUSE_CTRL_BRINGUP | Tests the bring-up sequence of the Fuse Controller by the MCU. |
MCU_LMEM_EXE | Tests execution by MCU from the MCU SRAM, contained inside the MCI component. LMEM refers to Local Memory, a generic alias for MCU SRAM. |
MCU_MCTP_SMOKE_TEST | Test verifies the I3C main target operation |
MCU_TEST_ROM_I3C_STREAMING_BOOT | Test verifies the I3C recovery target operation by using caliptra test ROM |
FUSE_PROV_WITH_LC_CTRL | Tests fuse provisioning in conjunction with the Lifecycle Controller. |
CALIPTRA_SS_LC_CTRL_BRINGUP | Tests the bring-up sequence of the Lifecycle Controller. |
CALIPTRA_SS_LC_CTRL_ST_TRANS | Validates state transitions of the Lifecycle Controller. |
Caliptra Core
Follow the link for Caliptra Core Integration Specification
MCU (Manufacturer Control Unit)
Overview
MCU is encapsulates VeeR EL2 core that includes an iCache, a dedicated DCCM, and AXI interfaces with separate AXI USER IDs to ROM and MCI. For more details refer to RISCV VeeR-EL2 Overview
Parameters & Defines
The VeeR EL2 core instance used for MCU has been configured with these options:
- DCCM size: 16KiB (see MCU DCCM SRAM Sizing)
- I-Cache depth: 16KiB
- ICCM: Disabled
- External Interrupt Vectors: 255
The following files from the Caliptra Subsystem repository contain the MCU configuration. Review these files for comprehensive list of MCU capabilities.
src/riscv_core/veer_el2/rtl/defines/css_mcu0_common_defines.vh
src/riscv_core/veer_el2/rtl/defines/css_mcu0_el2_param.vh
src/riscv_core/veer_el2/rtl/defines/css_mcu0_el2_pdef.vh
src/riscv_core/veer_el2/rtl/defines/defines.h
MCU Integration Requirements
-
Ensure Proper Memory Mapping
- The memory layout must match the physical memory configuration of the SoC.
- If modifications are required, update the base addresses and section placements accordingly.
- The address regions assigned to PIC and DCCM must not include any other AXI subordinates on the interconnect.
-
Enabling Programming interface.
- Please refer to section MCU Programming Interface for details on reference linker file for the MCU bringup.
MCU Core Configuration Customization
The MCU VeeR-EL2 core can be customized by integrators to optimize for specific SoC requirements.
Common Use Cases:
- Memory Architecture: Modify ICCM/DCCM addresses and sizes for SoC memory integration
- Power/Area Optimization: Remove or modify features (caching, number of interrupts)
- Performance Tuning: Adjust cache sizes and pipeline configurations for application workloads
Configuration Instructions:
Refer to the MCU Veer-EL2 Core Configuration section in the project README for complete step-by-step procedures.
Validation:
Execute the full regression test suite documented in How to test after any configuration changes to ensure system compatibility.
MCU DCCM SRAM Sizing
MCU's DCCM SRAM should be sized large enough to accommodate FW's stack and heap. If it is undersized, the MCU would have to rely on the MCU SRAM (accessed via AXI) for the stack/heap which has much lower performance.
MCU SRAM MRAC Considerations
The MCU's Memory Region Access Control (MRAC) regions are hard coded to 256MB boundaries. Each 256MB region is configured with uniform attributes - everything within a region is labeled as either "side effect" or "cachable". This affects how MCU SRAM and MCU MBOX SRAM (both located within MCI) should be integrated into the SoC memory map, as different components within MCI may require different access attributes.
Split Memory Mapping
Integrators have two main approaches for handling MCI memory mapping:
Option 1: Contiguous MCI Address Region - If integrators don't care about the MRAC limitations described in the following sections, they can use a standard contiguous MCI address map where all MCI components (including MCU SRAM and MCU MBOX SRAM) reside within a single 256MB region.
Option 2: Split Memory Mapping - Integrators can optionally split MCU SRAM and MCU MBOX SRAMs into their own dedicated 256MB regions, separate from other MCI components. This allows firmware to enable caching or disable side effects for these specific SRAMs.
Side Effect Considerations
When MCU SRAM and MCU MBOX SRAM remain within the main MCI address space (not split off), integrators should consider the following access limitations:
DWORD Access Requirement: MCI peripherals (MCI CSRs, MCU trace buffer CSRs, etc) require "side effect" attribute enabled. When "side effect" is enabled dword-aligned accesses are required. Unaligned accesses, like accessing a uint8_t, are not permitted and will result in a read fault error in the MCU.
If you want to avoid these DWORD alignment limitations and allow more flexible access patterns, you can choose to implement the Split Memory Mapping (Option 2) in your AXI interconnect for MCU SRAM and/or MCU MBOX SRAM. This allows the SRAMs to be placed in regions without the side effect attribute.
iCache Considerations
When MCU SRAM remains within the main MCI address space (not split off), integrators should consider the following caching limitations:
iCache Enablement Requirement: To enable MCU iCache, everything within the 256MB boundary containing MCU SRAM must be cachable. Since not all regions of MCI are cachable, MCU iCache cannot be enabled when using a contiguous MCI address map.
If you want to enable MCU iCache functionality, you must implement the Split Memory Mapping (Option 2) in your AXI interconnect. This allows MCU SRAM to be placed in a dedicated cachable region separate from other MCI components.
MCU Programming interface
MCU Linker Script Integration
This linker script defines the memory layout for the MCU firmware. It specifies the placement of various sections, ensuring proper memory mapping and execution flow.
Example Linker File can be found at : integration/test_suite/libs/riscv_hw_if/link.ld
By following this linker script configuration, the validation firmware can be correctly mapped and executed within the Caliptra Subsystem. Memory mapping for the validation firmware follows these principles:
- Instructions are stored in ROM for initial boot (.text)
- Read-only data is stored in ROM
- Stack and data sections are allocated to DCCM
- MCU SRAM is used for any combination of instructions and data to demonstrate program execution, data storage and persistence, streaming boot operations, and region protections that are enforced by MCI. Test firmware and corresponding linker scripts may be found in the repository test suites directory.
MCU External Interrupt Connections
| External interrupt vector | Description |
|---|---|
| 1 | MCI interrupts see MCI interrupt spec and MCI interrupt registers |
| 2 | I3C Interrupts |
| 255:3 | Exposed to SOC via cptra_ss_mcu_ext_int |
Fuse Controller
Overview
The Fuse Controller is a core component in the secure infrastructure of the system, responsible for managing the fuses and ensuring the integrity, consistency, and secure storage of sensitive data. It provides essential interfaces for direct fuse programming. The Fuse Controller interacts closely with the Lifecycle Controller (LC), FUSE macros, MCI, and Caliptra Core.
For an in-depth understanding of the Fuse Controller's functionality, including its programming flow, refer to Caliptra Subsystem Hardware Specification Document.
Parameters & Defines
| Parameter | Default | Description |
|---|---|---|
AlertAsyncOn | 5 | Enables asynchronous transitions on alerts. |
MemInitFile | "" | Hex file to initialize the OTP macro, including ECC. |
Interface
| Facing | Type | Width | Name | External Name in SoC Level | Description |
|---|---|---|---|---|---|
| External | Input | 1 | clk_i | cptra_ss_clk_i | Fuse Controller clock input. |
| External | Input | 1 | rst_ni | cptra_ss_rst_b_i | Reset signal input, active low. |
| Internal | Input | 1 | FIPS_ZEROIZATION_CMD_i | Fuse Zeroization request sampled on MCI reset deassertion. | |
| External | interface | 1 | core_axi_wr_req | cptra_ss_otp_core_axi_wr_req_i | AXI write request. |
| External | interface | 1 | core_axi_wr_rsp | cptra_ss_otp_core_axi_wr_rsp_o | AXI write response. |
| External | interface | 1 | core_axi_rd_req | cptra_ss_otp_core_axi_rd_req_i | AXI read request. |
| External | interface | 1 | core_axi_rd_rsp | cptra_ss_otp_core_axi_rd_rsp_o | AXI read response. |
| Internal | Output | 1 | intr_otp_operation_done_o | Indicates that the OTP operation has completed. | |
| Internal | Output | 1 | intr_otp_error_o | OTP error interrupt output (to be connected to MCI). | |
| Internal | Output | 5 | alerts | Alert signals for critical errors. | |
| Internal | Input | 1 | pwr_otp_i | OTP initialization request from the power manager. | |
| Internal | Output | Struct | pwr_otp_o | OTP response to the power manager. | |
| Internal | Input | Struct | lc_otp_vendor_test_i | Vendor test request input from LC Controller. | |
| Internal | Output | Struct | lc_otp_vendor_test_o | Vendor test response to LC Controller. | |
| Internal | Input | Struct | lc_otp_program_i | Lifecycle OTP programming request from LC Controller. | |
| Internal | Output | Struct | lc_otp_program_o | Lifecycle OTP programming response to LC Controller. | |
| Internal | Input | 1 | lc_dft_en_i | DFT enable input from LC Controller. | |
| Internal | Input | 1 | lc_escalate_en_i | Escalation enable input from LC Controller. | |
| Internal | Input | 1 | lc_check_byp_en_i | Clock bypass check enable input from LC Controller. | |
| Internal | Output | Struct | otp_lc_data_o | Lifecycle broadcasted data output to LC Controller. | |
| Internal | Output | Struct | otp_broadcast_o | FUSE broadcast output to Caliptra Core. This port broadcasts UDS-seed and Field-entropy-seed. |
Fuse Macro Memory Map and Fuse Controller CSR Address Map
The Caliptra Subsystem fuse controller supports a flexible and extensible memory map for storing one-time programmable (OTP) data. This structure is documented in the following files:
- Fuse Controller Register Map for registers.
- Fuse Macro Memory Map for fuse partition map.
The current fuse memory map consists of three main architectural segments: Caliptra-Core (prefix: CALIPTRA_CORE), Caliptra-Subsystem (prefix: CALIPTRA_SS), SoC/Vendor-Specific.
This structure enables separation of responsibilities and flexibility in SoC integration. While the Caliptra-Core fuse items are mandatory and must adhere to the Caliptra Fuse Map Specification, Caliptra-Subsystem fuses are required only when Caliptra is instantiated with Caliptra Subsystem. These Caliptra Subsystem fuses can also be configured based on SoC requirements. The SoC/Vendor-specific items can be customized based on integrator needs and product requirements. Therefore, the fields under SoC-specific categories can be resized or eliminated if unused.
SOC_SPECIFIC_IDEVID_CERTIFICATE Usage
This field defaults to 4 bytes but can be extended to accommodate storage of a full IDevID hybrid certificate (e.g., ML-DSA + ECC) if desired. Integrators must adjust its size in the YAML config used by the generation script.
FC Integration Requirements
Connectivity, Clock & Reset, Constraints & Violations
-
Connectivity:
- The Fuse Controller must interface seamlessly with the Fuse Macros, ensuring proper ECC support during programming and read operations.
- All AXI interfaces (
core_axi_wr_req,core_axi_rd_req) must follow the protocol specifications. - Inputs like
lc_otp_program_iandpwr_otp_ishould connect properly to the Lifecycle Controller (LC) and MCI respectively. - Alerts must propagate correctly to the system's alert manager for error handling.
-
Constraints & Violations:
- Any access to fuses must be gated by the
FUSE_CTRL_DIRECT_ACCESS_REGWENbit to prevent unauthorized writes. - There are some fuses that can be programmed only by Caliptra Core. Therefore, each AXI write should follow the access permission rule defined by
access_control_tableinsrc/fuse_ctrl/rtl/otp_ctrl_pkg.sv. - Timeout conditions during consistency checks (
FUSE_CTRL_CHECK_TIMEOUT) should trigger appropriate alerts. - Errors like invalid data, ECC failures, or access violations should raise alerts via the
alertssignal.
- Any access to fuses must be gated by the
-
Scan Path Exclusions:
- Ensure that secret fuse fields (UDS and Field-Entropy) and their corresponding flip-flops are not a parth of the scan path. Specifically, in this version, there are five OTP partitions that contain secrets that must not be leaked:
SECRET_MANUF_PARTITION: that contains the UDS seed, andSECRET_PROD_PARTITION_{0,1,2,3}: that each contain field entropy. These fuse partitions are "Buffered" partitions, meaning they are sensed at power-on and buffered in flip-flops until the next reset. During SoC integration it is vital to exclude the buffering flops that hold sensitive secret data from the scan chain to avoid leaks. These buffering flops may be excluded from the scan chain by excluding the following hierarchies:**::u_otp_ctrl::u_part_buf::u_otp_ctrl_ecc_reg::gen_partitions[X]::gen_buffered::u_part_buf::u_otp_ctrl_ecc_reg::**, for all values ofXin 1β5, since indices 1β5 map to the sensitive partitions mentioned above (defined in thePartInfolocalparam inotp_ctrl_part_pkg.sv, autogenerated from the OTP memory map HJSON). Additionally, these secrets are broadcasted with this input port:otp_broadcast_o. Therefore, this port, its propagated signals, and its driver signals must also not be scannable.
- Ensure that secret fuse fields (UDS and Field-Entropy) and their corresponding flip-flops are not a parth of the scan path. Specifically, in this version, there are five OTP partitions that contain secrets that must not be leaked:
- Since Fuse Controller scrambles secret partition with PRESENT chipher, this chiper keys, autogenerated from the OTP memory map HJSON, must be excluded from the scan path. Although these values are parameters defined in
otp_ctrl_part_pkg.sv, the registers driven by these parameters must be excluded from the scan path. - Note, the LC token partitions are not considered secret as only the cSHAKE128 hashes of each token are stored, not the raw tokens themselves.
Direct Access Interface
Fuse macros has to be programmed via the Direct Access Interface, which is comprised of the following CSRs:
| CSR Name | Description |
|---|---|
DIRECT_ACCESS_WDATA_0 | Low 32bit word to be written. |
DIRECT_ACCESS_WDATA_1 | High 32bit word to be written. |
DIRECT_ACCESS_RDATA_0 | Low 32bit word that has been read. |
DIRECT_ACCESS_RDATA_1 | High 32bit word that has been read. |
DIRECT_ACCESS_ADDRESS | byte address for the access. |
DIRECT_ACCESS_CMD | Command register to trigger a read or a write access. |
DIRECT_ACCESS_REGWEN | Write protection register for DAI. |
Initialization
The OTP controller initializes automatically upon power-up and is fully operational by the time the processor boots. The only initialization steps that SW should perform are:
- Check that the OTP controller has successfully initialized by reading
STATUS. I.e., make sure that none of the ERROR bits are set, and that the DAI is idle (STATUS.DAI_IDLE).- Choose whether the periodic background checks shall be subject to a timeout by programming a nonzero timeout cycle count to
CHECK_TIMEOUT. In this case, theCHECK_TIMEOUTregister must be set before theINTEGRITY_CHECK_PERIODandCONSISTENCY_CHECK_PERIODregisters (see next point). - Enable periodic background checks by programming nonzero mask values to
INTEGRITY_CHECK_PERIODandCONSISTENCY_CHECK_PERIOD. - It is recommended to lock down the background check registers via
CHECK_REGWEN, once the background checks have been set up
- Choose whether the periodic background checks shall be subject to a timeout by programming a nonzero timeout cycle count to
If needed, one-off integrity and consistency checks can be triggered via CHECK_TRIGGER.
If this functionality is not needed, it is recommended to lock down the trigger register via CHECK_TRIGGER_REGWEN.
Programming interface
The Fuse Controller (FC) programming interface is designed to manage lifecycle states, handle fuses with ECC support, and ensure secure interactions with the fuse macros. A key component in this architecture is the Fuse Controller Filter RTL. This module intercepts and verifies the fuse programming sequence by checking that all parts of the transaction originate from the same authorized source. In doing so, the filter guarantees that fuse provisioning is performed in an atomic manner.
Atomic fuse provisioning means that only one entity can initiate the programming sequence at a time. The entire sequenceβdata write, address write, and command writeβmust complete successfully. If any phase fails or if an inconsistency is detected (for example, if the AXI user ID changes between phases), the operation is aborted, and a cold reset is required before any new programming attempt can be made.
The access control table, which defines allowed fuse address ranges along with the corresponding authorized AXI user IDs. See access_control_table in src/fuse_ctrl/rtl/otp_ctrl_pkg.sv.
Below are the key operations supported by the programming interface:
-
Direct Access Interface (DAI):
- Registers:
FUSE_CTRL_DIRECT_ACCESS_CMD: Specifies the operation (FUSE_CTRL_CMD_DAI_WRITEfor write,FUSE_CTRL_CMD_DAI_READfor read).FUSE_CTRL_DIRECT_ACCESS_ADDRESS: Specifies the fuse memory address to access.FUSE_CTRL_DIRECT_ACCESS_WDATA_0: Write data (32-bit granularity).FUSE_CTRL_DIRECT_ACCESS_WDATA_1: Write data for 64-bit operations.FUSE_CTRL_DIRECT_ACCESS_RDATA_0: Read data (32-bit granularity).FUSE_CTRL_DIRECT_ACCESS_RDATA_1: Read data for 64-bit operations.
- Procedure:
- Write the address to
FUSE_CTRL_DIRECT_ACCESS_ADDRESS. - For write operations:
- Populate
FUSE_CTRL_DIRECT_ACCESS_WDATA_0(andFUSE_CTRL_DIRECT_ACCESS_WDATA_1for 64-bit operations).
- Populate
- Set the command in
FUSE_CTRL_DIRECT_ACCESS_CMD. - Wait for the operation to complete by polling the
DAI_IDLEbit inFUSE_CTRL_STATUS.
- Write the address to
- ECC Support:
- ECC is automatically applied during programming to ensure data integrity.
- Registers:
-
Digest Calculation:
- Used to lock a partition after programming is complete.
- Registers:
FUSE_CTRL_DIRECT_ACCESS_CMD: Use command0x4for digest calculation.FUSE_CTRL_DIRECT_ACCESS_ADDRESS: Partition base address.
- Procedure:
- Write the partition base address to
FUSE_CTRL_DIRECT_ACCESS_ADDRESS. - Trigger the digest calculation command (
0x4) inFUSE_CTRL_DIRECT_ACCESS_CMD. - Poll the
DAI_IDLEbit inFUSE_CTRL_STATUSto confirm the operation is complete.
- Write the partition base address to
Readout Sequence
A typical readout sequence looks as follows:
- Check whether the DAI is idle by reading the
STATUSregister. - Write the byte address for the access to
DIRECT_ACCESS_ADDRESS. Note that the address is aligned with the granule, meaning that either 2 or 3 LSBs of the address are ignored, depending on whether the access granule is 32 or 64bit. - Trigger a read command by writing 0x1 to
DIRECT_ACCESS_CMD. - Poll the
STATUSuntil the DAI state goes back to idle. Alternatively, theotp_operation_doneinterrupt can be enabled up to notify the processor once an access has completed. - If the status register flags a DAI error, additional handling is required.
- If the region accessed has a 32bit access granule, the 32bit chunk of read data can be read from
DIRECT_ACCESS_RDATA_0. If the region accessed has a 64bit access granule, the 64bit chunk of read data can be read from theDIRECT_ACCESS_RDATA_0andDIRECT_ACCESS_RDATA_1registers. - Go back to the first step and repeat until all data has been read.
The hardware will set DIRECT_ACCESS_REGWEN to 0x0 while an operation is pending in order to temporarily lock write access to the CSRs registers.
Sequences: Reset, Boot
-
Reset Sequence:
- De-assert
rst_niafter the primary clock (clk_i) stabilizes. - Verify reset state by reading
FUSE_CTRL_STATUS. All errors in the status register should be 0. - Ensure Fuse Macros are in their default state after reset.
- De-assert
-
Boot Sequence:
- Initialize Fuse Macros by programming essential fuses using the programming interface.
- Perform a full integrity check by triggering
FUSE_CTRL_CHECK_TRIGGERand ensure the system is error-free before proceeding. - Validate readiness by checking the
FUSE_CTRL_STATUSregister.
UDS & Field Entropy FIPS Zeroization Sequence
This sequence follows the "theory of operation" stated in this 'fuse-zeroization-programmer's-guide'
Follow these steps in order to correctly zeroize the fuses and verify the operation for any partition that requires FIPS zeroization to be set (determined by zeroizable flag when a partition is generated).
- Assert Physical Presence: Set the FIPS_zeroization_PPD pin high before taking the Caliptra subsystem out of reset. This confirms physical presence and authorizes the zeroization. When this signal is asserted, it triggers preemptive zeroization of secret FUSEs. The MCU ROM samples
cptra_ss_FIPS_ZEROIZATION_PPD_iby reading the corresponding register storing its value in MCI. Ifcptra_ss_FIPS_ZEROIZATION_PPD_i == HIGH, the MCU ROM writes32'hFFFF_FFFFto thess_soc_MCU_ROM_zeroization_mask_regregister of MCI. If this mask register is not set by MCU, the zeroization request is aborted by the fuse controller. - Issue Zeroization Commands: Trigger zeroization by sending a zeroization command to Caliptra core. Caliptra core will send a sequence of DAI (Direct Access Commands) commands to the fuse controller to perform the zeroization. The recommended order is:
- Clear the Partition Zeroization Flag: First, send a DAI command to clear this 64-bit flag within the target partition. Executing this step first is critical, as it masks potential ECC or integrity errors if the process is interrupted by a power failure.
- Zeroize Data Words: Send DAI zeroization commands for all data words within the partition.
- Clear the Partition Digest: Finally, send a DAI command to clear the partition's digest.
- Power Cycle the SOC (including Caliptra SS): Apply a cold reset to the Caliptra subsystem. FIPS_zeroization_PPD pin should now be cleared (set low).
- Verify the Operation: From the main MCU, read the partition's digest value from the associated fuse_ctrl digest registers.
- Success: If the register returns the expected zeroized digest value, the operation is complete.
- Failure: If the digest does not match the zeroized value, repeat the entire sequence starting from Step 1.
FIPS Zeroization Sequence For ECC
Zeroization is implemented within the fuse controller RTL module. It is therefore the integratorβs responsibility to ensure that the ECC bits in the corresponding fuse partition are also zeroized when a zeroization command is issued to the fuse macro. To achieve this, the integrator must provide a dedicated implementation in the fuse macro wrapper to handle zeroization of the ECC bits.
Miscellanious Fuse Integration Guidelines
- If there is a provisioning step where SW (non-secret) and secret partitions need to be programmed within the same reset/power cycle of a SOC, then SW partition needs to be programmed first
- Whenever a secret partition is programmed, it requires a FC reset, implying it requires a SOC reset
- ECC bits inside fuse macros MUST be zeroized per FIPS guidelines. Since these bits are implemented by SOC a a part of OTP gasket, SOC should also implement FIPS zeroization of the ECC for UDS, FE, Ratchet Seeds (OCP lock), any vendor secrets (if required by FIPS).
- FIPS zeroization of the ECC bits of a given partition must be done after the FIPS zeroization of the partition data, zeroization marker and digest.
- UDS & FE MUST ONLY be FIPS zeroized by Caliptra Core (by Subsystem default design construction doesnt allow anyone else to do this operation).
- DAI Command Error Checking: The Caliptra core is responsible for checking the result of each DAI zeroization command to ensure it completed successfully. Any errors must be handled appropriately.
- Partitions 0-5 should not be changed by SOC. Donβt add or remove any fields, re-adjust sizes of these partitions as Caliptra ROM may expect them to be of a fixed size.
- Fuse Macro Wrapper Requirements: The fuse_ctrl macro wrapper must implement a retry mechanism for the zeroization process. To prevent damage to the fuses, the wrapper must also avoid double writes to bits that have already been programmed. Please follow the specific integration guidelines provided by your fuse macro vendor.
- OCP Lock ratchet seeds can be FIPS zeroized by MCU
- If FIPS zeroization is required for Vendor Secret Partitions, then SOC shall generate the partition with zeroization flag, validate that the zeroization sequence documented above works as expected and uses MCU to do the FIPS zeroization. Any additional physical security protection of this partition is SOC's responsibility since the use cases are SOC defined.
How to test : Smoke & more
The smoke test focuses on ensuring basic functionality and connectivity of the FC & LCC. TODO More details will be provided once FC is ready to test.
Generating the Fuse Partitions
The configurable parts of the fuse_ctrl, specifically the fuse map and register interface,
are bootstrapped through a separate script ./tools/scripts/fuse_ctrl_script/gen_fuse_ctrl_partitions.py.
For a detailed breakdown of the design rationale behind the script as well as execution instructions,
refer to Fuse Map Generation Script.
Fuse Controller Macro
The following integration section is based on the Generalized Open-source Interface with modifications to match the Caliptra implementation.
Overview
The Fuse Controller Macro implements a generalized open-source interface for functional operation (described below). Any OTP redundancy mechanism like per-word ECC is assumed to be handled inside the wrapper, which means that the word width exposed as part of the generalized interface is the effective word width.
Paramteres & Defines
| Parameter | Default | Description |
|---|---|---|
Width | 16 | Width of native OTP words. |
AddrWidth | Derived | Width of the address signal, derived from Depth. |
Depth | `2**OtpAddrWidth | Depth of OTP macro. |
CmdWidth | 7 | Width of the OTP command. Sparsely encoded. |
ErrWidth | 3 | Width of error code output signal. |
SizeWidth | 2 | Width of the size input field. Allows to transfer up to 4 native OTP words at once. |
PwrSeqWidth | "" | Hex file to initialize the OTP macro, including ECC. |
IfWidth | 2**OtpSizeWidth*OtpWidth | Width of the wrapper data interface. |
FC Macro Integration Requirements
The generalized open-source interface is a simple command interface with a ready / valid handshake that makes it possible to introduce back pressure if the OTP macro is not able to accept a command due to an ongoing operation.
In order to facilitate the scrambling and digest operations, the data width has been sized such that data blocks up to the PRESENT block size (64bit) can be transferred across the generalized interface. The actual size of a transfer is determined via the size_i field. Transfer sizes are specified in multiples of the native OTP block size, as listed below.
Value of size_i | #Native OTP Words | Bit Slice |
|---|---|---|
| 2'b00 | 1 | {word0} = data[15:0] |
| 2'b01 | 2 | {word1, word0} = data[31:0] |
| 2'b10 | 3 | {word2, word1, word0} = data[47:0] |
| 2'b11 | 4 | {word3, word2, word1, word0} = data[63:0] |
Responses are returned in-order via an unidirectional response interface (i.e., without back pressure capability). Downstream logic must be able to sink the response in any case. The response optionally carries read data, depending on whether the operation that took place was a read or not. Also, an error signal returns a non-zero error code in case an error occurred while carrying out the OTP command.
The signals pertaining to the generalized open-source interface are listed below.
The signals are exposed as cptra_ss_fuse_macro_outputs_i and
cptra_ss_fuse_macro_inputs_o at the top level.
| Signal | Type | Width | Description |
|---|---|---|---|
cptra_ss_fuse_macro_inputs_o.valid_i | Input | 1 | Valid signal for the command handshake. |
cptra_ss_fuse_macro_inputs_o.size_i | Input | [SizeWidth-1:0] | Number of native OTP words to transfer, minus one: 2'b00 = 1 native word ... 2'b11 = 4 native words. |
cptra_ss_fuse_macro_inputs_o.cmd_i | Input | [CmdWidth-1:0] | OTP command: 7'b1111010 = read, 7'b1001001 = write, 7'b1010100 = read raw, 7'b1100111 = write raw, 7'b0100000 = initialize, 7'b0111101 = zeroize |
cptra_ss_fuse_macro_inputs_o.addr_i | Input | [$clog2(Depth)-1:0] | OTP word address. |
cptra_ss_fuse_macro_inputs_o.wdata_i | Input | [IfWidth-1:0] | Write data for write commands. |
cptra_ss_fuse_macro_outputs_i.fatal_alert_o | Output | 1 | Fatal alert output from the FC macro. This is connected to a separate alert channel in the instantiating IP. The instantiating IP latches the alert indication and continuously outputs alert events until reset. |
cptra_ss_fuse_macro_outputs_i.ecov_alert_o | Output | 1 | Recoverable alert output from the FC macro. This is connected to a separate alert channel in the instantiating IP. Should only be pulsed high for each alert occurrence. The instantiating IP then sends out a single alert event for each pulse. |
cptra_ss_fuse_macro_outputs_i.ready_o | Output | 1 | Ready signal for the command handshake. |
cptra_ss_fuse_macro_outputs_i.valid_o | Output | 1 | Valid signal for command response. |
cptra_ss_fuse_macro_outputs_i.rdata_o | Output | [IfWidth-1:0] | Read data from read commands. |
cptra_ss_fuse_macro_outputs_i.err_o | Output | [ErrWidth-1:0] | Error code. |
The write raw and read raw command instructs the Fuse Controller Macro
wrapper to store / read the data in raw format without generating nor checking
integrity information. That means that the wrapper must return the raw,
uncorrected data and no integrity errors.
The zeroize command instructs the Fuse Macro wrapper to "erase" the addressed
value. As fuses cannot be unset, the typical erase behavior is to set all fuses
of the addressed value to 1, ideally including the ECC bits.
The Fuse Controller Macro wrapper implements the error codes (0x0 - 0x4).
| Error Code | Enum Name | Recoverable | DAI | LCI | Unbuf | Buf | Description |
|---|---|---|---|---|---|---|---|
| 0x0 | NoError | - | x | x | x | x | No error has occurred. |
| 0x1 | MacroError | no | x | x | x | x | Returned if the OTP macro command did not complete successfully due to a macro malfunction. |
| 0x2 | MacroEccCorrError | yes | x | - | x | x | A correctable ECC error has occurred during a read operation in the OTP macro. |
| 0x3 | MacroEccUncorrError | no | x | - | x* | x | An uncorrectable ECC error has occurred during a read operation in the OTP macro. Note (*): This error is collapsed into MacroEccCorrError if the partition is a vendor test partition. It then becomes a recoverable error. |
| 0x4 | MacroWriteBlankError | yes / no* | x | x | - | - | This error is returned if a write operation attempted to clear an already programmed bit location. Note (*): This error is recoverable if encountered in the DAI, but unrecoverable if encountered in the LCI. |
The timing diagram below illustrates the timing of a command. Note that both read and write commands return a response, and each command is independent of the previously issued commands. The latency from accepting a command to returning a response depends on the underlying OTP IP and is typically larger than 10 cycles. The returned values depend on the command type and whether an error occurred or not.

Note that the default configuration of the Fuse Controller allows up to two outstanding OTP commands, meaning that it is permissible to acknowledge an incoming command and start working on it while the results of the last command are still in the process of being output (e.g., due to an output register stage).
The Fuse Controller Macro uses the following signals to interface with the Fuse Controller.
Generic Strap Port Usage for FC Register Locations
To support flexible integration across varying fuse partition generations, Caliptra ROM uses two generic strap ports to locate fuse controller registers.
Why These Straps Are Needed
Each fuse partition generation introduces new error bits into the status register. This causes the idle bit to shift leftward, changing its position within the SOC_OTP_CTRL_STATUS register. Similarly, the CMD register may shift downward in memory if new fuse partition definitions introduce additional registers like ADDR, WDATA0, RDATA0, etc.
Because of these dynamic shifts:
- The idle bit location cannot be hardcoded.
- The CMD register address must be explicitly provided, even though the other fuse controller registers are laid out consecutively.
Strap Definitions
-
cptra_ss_strap_generic_0_i
A 32-bit input strap that encodes:- Upper 16 bits: Bit index of the idle status bit (
IDLE_BIT_STATUS) withinSOC_OTP_CTRL_STATUS. - Lower 16 bits: Offset address of
SOC_OTP_CTRL_STATUSwithin theSOC_IFC_REGspace, relative toSOC_OTP_CTRL_BASE_ADDR.
This allows the ROM to accurately monitor the fuse controller's idle state regardless of partition-induced shifts.
- Upper 16 bits: Bit index of the idle status bit (
-
cptra_ss_strap_generic_1_i
A 32-bit input strap that provides the address of the CMD register.
Since the fuse controller registers are laid out consecutively, specifying the CMD register is sufficient for the ROM to infer the locations of adjacent registers likeADDR,WDATA0, andRDATA0.
FC Macro Test Interface
The Fuse Controller Macro requires a test interface to be able to access the underlying OTP debug access interface and power manager controller. During early manufacturing, the test interface can be used to run BIST and repair flows before performing a non-volatile raw unlock operation.
For security reasons, it must be ensured that it is not possible to read / program arbitrary OTP memory locations through the test interface. Only specific, pre-defined test locations shall be readable and programmable. Access to debug access interface must also be disabled once the device is in mission mode (i.e. PROD life cycle state).
Life Cycle OTP Programming Behavior and Integrator Responsibilities
During a lifeβcycle transition, the Caliptra Life Cycle Controller performs two OTP write operations to the transitionβcounter and lifeβcycleβstate fields. This behavior is architecturally defined and required for secure, faultβresistant state progression. Although only one field changes in each phase, both fields reside within the same OTP word, so the macro receives two programming operations that may include writing some bits to the same value they already hold. This programming pattern is expected and safe for OTP implementations that correctly support wordβlevel writes, including rewriting a bit with the same value (1 -> 1).
Integrators must ensure that their OTP macro or wrapper supports rewriting fields without generating errors, and that the macroβs burn semantics align with Caliptraβs assumption that βburnβ corresponds to writing a logical 1. If an OTP vendor interprets 0 as a burn operation or cannot tolerate 1 -> 1 writes, the integrator must adapt their wrapperβfor example, by inverting the encoding or using perβbit writeβenableβto ensure compatibility.
Life Cycle Controller
Overview
The LC Controller (Lifecycle Controller) is a critical component of the Caliptra Subsystem, responsible for securely managing the lifecycle states of the chip. The LC Controller interacts with other subsystems such as the Fuse Controller, MCI, AXI interconnect, and JTAG TAP to enforce secure transitions, validate tokens, and generate error conditions. Additionally, it implements escalation mechanisms to respond to security breaches, enabling the chip to enter secure states like SCRAP.
For a detailed description of the Lifecycle Controller's architecture, design, and operational flow, refer to Caliptra Subsystem Hardware Specification Document.
Parameters & Defines
| Parameter | Default (Max) | Description |
|---|---|---|
AlertAsyncOn | 2'b11 | |
IdcodeValue | 32'h00000001 | Idcode for the LC JTAG TAP. |
RndCnstLcKeymgrDivInvalid | (see RTL) | Diversification value used for all invalid life cycle states. |
RndCnstLcKeymgrDivTestUnlocked | (see RTL) | Diversification value used for the TEST_UNLOCKED* life cycle states. |
RndCnstLcKeymgrDivDev | (see RTL) | Diversification value used for the DEV life cycle state. |
RndCnstLcKeymgrDivProduction | (see RTL) | Diversification value used for the PROD/PROD_END life cycle states. |
RndCnstLcKeymgrDivRma | (see RTL) | Diversification value used for the RMA life cycle state. |
Interface
| Facing | Type | width | Name | External Name in SoC Level | Description |
|---|---|---|---|---|---|
| External | input | 1 | clk_i | cptra_ss_clk_i | clock |
| External | input | 1 | rst_ni | cptra_ss_rst_b_i | LC controller reset input, active low |
| External | input | 1 | lc_sec_volatile_raw_unlock_en_i | cptra_ss_lc_sec_volatile_raw_unlock_en_i | Enables Volatile TEST_UNLOCKED0 state transition infra |
| External | input | 1 | raw_unlock_token_hashed_i | cptra_ss_raw_unlock_token_hashed_i | Hashed token for RAW unlock |
| External | input | 1 | Allow_RMA_or_SCRAP_on_PPD | cptra_ss_lc_Allow_RMA_or_SCRAP_on_PPD_i | This is GPIO strap pin. This pin should be high until LC completes its state transition to RMA or SCRAP. |
| External | interface | 1 | axi_wr_req | cptra_ss_lc_axi_wr_req_i | LC controller AXI write request input |
| External | interface | 1 | axi_wr_rsp | cptra_ss_lc_axi_wr_rsp_o | LC controller AXI write response output |
| External | interface | 1 | axi_rd_req | cptra_ss_lc_axi_rd_req_i | LC controller AXI read request input |
| External | interface | 1 | axi_rd_rsp | cptra_ss_lc_axi_rd_rsp_o | LC controller AXI read response output |
| External | interface | 1 | jtag_i | cptra_ss_lc_ctrl_jtag_i | LC controller JTAG input ports |
| External | interface | 1 | jtag_o | cptra_ss_lc_ctrl_jtag_o | LC controller JTAG output ports |
| External | input | 1 | scan_rst_ni | cptra_ss_lc_ctrl_scan_rst_ni_i | LC controller scan reset input, active low |
| Internal | output | 3 | alerts | Alert outputs generated by LCC if there is an error due to one of following: register bus, lc state and fuse programming | |
| External | input | 1 | esc_scrap_state0 | cptra_ss_lc_esclate_scrap_state0_i | An escalation input that leads LC controller to enter into SCRAP mode |
| External | input | 1 | esc_scrap_state1 | cptra_ss_lc_esclate_scrap_state1_i | An escalation input that eads LC controller to enter into SCRAP mode |
| Internal | input | 1 | pwr_lc_i | A power initilization input coming from MCI | |
| Internal | struct | 1 | pwr_lc_o | Two outputs show: (i) LC controller can accept a request, (ii) LC is initialized. | |
| Internal | struct | 1 | lc_otp_vendor_test_o | Access to fuse controller for vendor test partitions | |
| Internal | struct | 1 | lc_otp_vendor_test_i | Access to fuse controller for vendor test partitions | |
| Internal | struct | 1 | lc_otp_program_o | Programming interface to fuse controller to update LCC state and couter | |
| Internal | struct | 1 | lc_otp_program_i | Programming interface from fuse controller to update LCC state and couter | |
| Internal | struct | 1 | otp_lc_data_i | Broadcasted values from the fuse controller | |
| Internal | output | 1 | lc_dft_en_o | DFT enable to MCI | |
| Internal | output | 1 | lc_hw_debug_en_o | CLTAP enable to MCI | |
| Internal | output | 1 | lc_escalate_en_o | cptra_ss_lc_escalate_en_o | Broadcast signal to promote esclation in SoC |
| Internal | output | 1 | lc_check_byp_en_o | cptra_ss_lc_check_byp_en_o | External clock status delivery signal to fuse controller |
| External | output | 1 | lc_clk_byp_req_o | cptra_ss_lc_clk_byp_req_o | A request port to swtich from LCC clock to external clock |
| External | input | 1 | lc_clk_byp_ack_i | cptra_ss_lc_clk_byp_ack_i | Acknowledgment signal to indicate external clock request is accepted |
| Internal | input | 1 | otp_device_id_i | Unused port | |
| Internal | input | 1 | otp_manuf_state_i | Unused port | |
| Internal | output | 1 | hw_rev_o | Unused port | |
| Internal | input | 253 | cptra_ss_mcu_ext_int | Reflection of HW revision ID read from fuse controller |
Fuse Macro Memory Map / Fuse Controller CSR Address Map
See Life-cycle Controller Register Map.
LC Integration Requirements
Connectivity, Clock & Reset, Constraints & Violations
-
Connectivity:
- Ensure proper routing of all signals to avoid conflicts with other modules.
- Interfaces like
jtagandaximust adhere to the defined protocol specifications. - Escalation signals (
esc_scrap_state0andesc_scrap_state1) brings LC controller into temporal SCRAP mode (Escalation state) and therefore needs to be connected to a dedicated controller. Allow_RMA_or_SCRAP_on_PPDneeds to be tied 0 if it is not being used. Otherwise, it might break LC controller's internal FSM.- Avoid glitches on
Allow_RMA_or_SCRAP_on_PPDand escalation inputs (esc_scrap_state0,esc_scrap_state1) that could cause unintended transitions. - Verify that all output signals, including alerts, remain within the expected ranges under normal operation.
-
IMPORTANT SOC REQUIREMENT:
- Life cycle controller allows you to switch from internal to external clock on a request from SOC over JTAG or AXI (As explained in other sections, this is typically used for Fuse programming scenarios when a stable clock is not yet available within the SOC). When the request arrives, life cycle controller requests the SOC to do the clock switch. When such a request is made, SOC MUST respond with an acknowledgement within 2 clock cycles of the internal clock. If this condition is not met, OTP controller will assert "program error" and the SOC must go through a reset cycle and redo the above steps. This must be verified by the SOC as a part of Caliptra subsystem integration checks.
To protect from clock stretching attacks Caliptra mandates using a clock source that is constructed within the SOC (eg. PLL, Calibrated Ring Oscillator, etc). For such a clock source, a SOC may require fuses to be programmed. TP programming demands a reliable and deterministic clock signal to ensure correct fuse write operations; which SOC may not have during the early phases of manufacturing flow due to above constraints. In order to overcome this issue, this
external clockcan be used typically in the manufacturing phase of a SOC; and for such SOCs this external clock is supplied from a platform (e.g an ATE). Since the Caliptra subsystem includes only one clock input (cptra_ss_clk_i), the SoC integrator is responsible for ensuring that this input can be switched to a stable source.The Life-cycle Controller requires a token to execute conditional state transitions. All tokens reside within a single partition, which the integrator can lock only once. Therefore, if any required tokens are not programmed before the partition is locked, they will remain at their default value of 0 and cannot be updated afterward.
- The life-cycle controller exposes an
LC_STATEregister that carries the life-cycle controller state, which the SoC can read to determine the current life-cycle state. In addition, the Caliptra Subsystem top level provides thecaliptra_ss_life_cycle_steady_state_oandcaliptra_ss_otp_state_valid_osignals, which are broadcast from the fuse controller. Whenevercaliptra_ss_otp_state_valid_ois asserted,caliptra_ss_life_cycle_steady_state_oreflects the latest life-cycle state stored in the fuse macro in the following cycle. Because the fuse controller is initialized earlier than the life-cycle controller, these broadcast state signals are derived from the fuse controller. Note thatcaliptra_ss_otp_state_valid_ois driven low by the fuse controller if a fatal error occurs in the fuse controller or if an escalation signal is asserted by the life-cycle controller. In contrast,LC_STATEprovides the life-cycle controllerβs own view of the state, independent of the fuse controllerβs errors such as entering SCRAP state.
Volatile-unlock state transitions are not reflected by the fuse controller, and therefore
caliptra_ss_life_cycle_steady_state_oandcaliptra_ss_otp_state_valid_odo not capture state transitions granted exclusively by the life-cycle controller. To cover this case, the Caliptra Subsystem also broadcastscaliptra_ss_volatile_raw_unlock_success_o, which is asserted by the life-cycle controller to indicate that the volatile-unlock state has been granted. -
Scan Path Exclusions:
- Ensure that the RAW_UNLOCK token is excluded from the scan chain. This token is different from other LC transition tokens as it is stored in the in gates but in a hashed form as other tokens. It is recommended to exclude since it is not provisioned through fuse macro as other tokens.
To exclude it from scan, the following hierarchies must be excluded:
*::lc_ctrl_fsm::hashed_tokens_{higher, lower}[RawUnlockTokenIdx]and*::lc_ctrl_fsm::hashed_token_mux.
- Ensure that the RAW_UNLOCK token is excluded from the scan chain. This token is different from other LC transition tokens as it is stored in the in gates but in a hashed form as other tokens. It is recommended to exclude since it is not provisioned through fuse macro as other tokens.
To exclude it from scan, the following hierarchies must be excluded:
-
RAW Unlock Token:
- The
cptra_ss_raw_unlock_token_hashed_itop-level input defines the hashed value of the RAW unlock token. The hashed value is generated from the unhashed RAW unlock token, which the integrator must obtain from a cryptographically secure random number generator. Both the hashed and the unhashed token must be kept secret. To generate the hashed token (and optionally also the unhashed token),tools/scripts/lc_ctrl_script/gen_lc_ctrl_token.pycan be used. - The unhashed RAW unlock token is required in SW that performs a non-volatile RAW unlock.
- The hashed RAW unlock token is stored in HW (see first bullet point) and required in SW that performs a volatile RAW unlock.
- The
Programming Interface
The LC Controller's programming interface facilitates lifecycle state transitions, secure token authentication, and system initialization. Below are the key programming steps:
-
Initialization:
- Ensure the LC Controller is ready by polling the
LC_CTRL_STATUS_OFFSETregister for theREADY_MASKbit. - Verify initialization is complete using the
INIT_MASKbit in the same register. - Corresponding fuse partitions need to be provisioned in order to perform state transitions
- Ensure the LC Controller is ready by polling the
-
Lifecycle State Transitions:
- Claim the transition mutex by writing
0x96(MuBi8True) toLC_CTRL_CLAIM_TRANSITION_IF_OFFSETand polling until the value is correctly latched. - Set the desired next lifecycle state by writing to
LC_CTRL_TRANSITION_TARGET_OFFSET. - Write the 128-bit transition token (if required) into the
LC_CTRL_TRANSITION_TOKEN_*_OFFSETregisters. - Trigger the state transition by writing
0x1toLC_CTRL_TRANSITION_CMD_OFFSET. - Poll the
LC_CTRL_STATUS_OFFSETregister to monitor for successful state transition or detect errors such as token errors, OTP errors, or RMA strap violations. - Each TEST_UNLOCKED state has its own TOKEN (see See Fuse Memory Map).
- During a state transition, an asserted reset or zeorization command can cause permanent life-cycle state corruption.
- Claim the transition mutex by writing
-
Token Validation:
- For conditional state transitions, provide the transition token before the transition request.
- Toke Format is illustrated with a python implementation:
# value = 0x318372c87790628a05f493b472f04808
# data = value.to_bytes(16, byteorder='little')
# custom = 'LC_CTRL'.encode('UTF-8')
# shake = cSHAKE128.new(data=data, custom=custom)
# shake output is 0x4c9ca068a68474d526e7d8a0233d5aad
# To unlock a state having the shake condition above, the LCC needs
# to get the following input set:
TOKEN_write(LC_CTRL_TRANSITION_TOKEN_3_OFFSET, 0x318372c8)
TOKEN_write(LC_CTRL_TRANSITION_TOKEN_1_OFFSET, 0x7790628a)
TOKEN_write(LC_CTRL_TRANSITION_TOKEN_2_OFFSET, 0x05f493b4)
TOKEN_write(LC_CTRL_TRANSITION_TOKEN_0_OFFSET, 0x72f04808)
- RMA and SCRAP Strap Handling:
- Ensure the
Allow_RMA_or_SCRAP_on_PPDGPIO strap is asserted for RMA or SCRAP transitions. Transitions without this strap will fail with an appropriate status in theLC_CTRL_STATUS_OFFSETregister.
- Ensure the
Sequences: Reset, Boot
-
Reset Sequence:
- Bring the LC Controller out of reset by asserting and de-asserting
rst_niafter clock stabilization. - Perform a reset sequence after each state transition routine
- Bring the LC Controller out of reset by asserting and de-asserting
-
Boot Sequence:
- Enable MCI that intilaize the LC controller.
- Verify successful initialization by reading
LC_CTRL_STATUS_OFFSET.
-
Error Scenarios:
- Test scenarios where invalid tokens, Fuse errors, or missing RMA straps are injected to validate error handling and system recovery mechanisms.
-
MCI Masking Registers for LCC Decoding Signals:
- The MCI provides a set of masking registers that allow the SoC integrator to explicitly masks Caliptra Coreβdebug level, SOC_DFT_EN and SOC_HW_DEBUG_EN. Caliptra Core expresses its debug grant through the
ss_soc_dbg_unlock_level_ivector, where each bit represents a distinct debug unlock level. These requests are not acted upon directly; instead, they are first AND-masked with SoC-programmed MCI registers to ensure that only integrator-approved debug levels can be enabled. - For production debug unlock, the integrator must program
MCI_REG_SOC_PROD_DEBUG_STATE_0andMCI_REG_SOC_PROD_DEBUG_STATE_1MCI registers. Together, these registers form a 64-bit mask that gatesss_soc_dbg_unlock_level_i. A debug level is considered enabled only if the corresponding bit is set both in Caliptra Coreβs unlock request vector and in the SoC-programmed mask. For example, if Caliptra Core asserts the fifth debug level by settingss_soc_dbg_unlock_level_i[4], the integrator must also set bit ofMCI_REG_SOC_PROD_DEBUG_STATE[1:0][4]for that level to take effect. - The same masking mechanism applies to SOC_DFT_EN enable and SOC_HW_DEBUG_EN. For these, MCI offers
MCI_REG_SOC_DFT_EN_0,MCI_REG_SOC_DFT_EN_1andMCI_REG_SOC_HW_DEBUG_EN_0,MCI_REG_SOC_HW_DEBUG_EN_1mask registers. These are also masked withss_soc_dbg_unlock_level_i. If this masking (AND operation) results in a value that has1in it. The corresponding enable signal is set to high.
- The MCI provides a set of masking registers that allow the SoC integrator to explicitly masks Caliptra Coreβdebug level, SOC_DFT_EN and SOC_HW_DEBUG_EN. Caliptra Core expresses its debug grant through the
How to Test: Smoke & More
Smoke Test
-
Basic Connectivity:
- Verify the LC Controller responds to read and write operations on key registers (e.g.,
LC_CTRL_STATUS_OFFSET,LC_CTRL_CLAIM_TRANSITION_IF_OFFSET).
- Verify the LC Controller responds to read and write operations on key registers (e.g.,
-
Basic Initialization:
- Check that the
READY_MASKandINIT_MASKbits inLC_CTRL_STATUS_OFFSETtransition to the expected values during initialization.
- Check that the
-
Lifecycle Transition:
- Perform a single state transition (e.g.,
RAWtoTEST_UNLOCKED0)
- Perform a single state transition (e.g.,
Functional Tests
-
Full Lifecycle Sequence:
- Run all lifecycle transition functions and validate each transition step via the status register and debug messages.
-
Error Injection:
- Test token errors by providing invalid tokens during a transition request.
- Simulate OTP errors by corrupting OTP data or configuration.
- Test RMA transitions with and without the
Allow_RMA_or_SCRAP_on_PPDGPIO strap. - Test SCRAP transitions with and without the
Allow_RMA_or_SCRAP_on_PPDGPIO strap.
-
Boundary Testing:
- Verify correct operation under boundary conditions, such as repeated transitions, simultaneous requests, or rapid reset sequences.
Advanced Tests
-
Stress Test:
- Perform rapid transitions through multiple lifecycle states to validate system robustness.
- Simulate power interruptions during critical operations.
-
Integration Tests:
- Verify interaction with other modules such as the fuse controller and MCI during state transitions.
MCI
Overview
Manufacturer Control Interface provides the following features for Caliptra SS:
-
Boot Sequencer for the SS
-
FC Init
-
LCC Init
-
MCU Reset
- Hitless Update Capability
-
-
Caliptra Reset
-
MCU SRAM
-
MCU Trace Buffer
-
MCU watchdog timer
-
Mailboxes
-
LCC State Translator for Caliptra Core
-
Error Aggregation
-
Register Bank for MCU/SOC
The Boot Sequence is what brings the subsystem up. It will do fuse controller and life cycle controller initialization. It then brings up MCU and Caliptra based on the breakpoint and no rom config input pins. Once MCI has done the subsystem bring up, it provides other functionality like the MCU SRAM, DAM for MCU, Error aggregation for the SS and more.
If there is an issue within MCI whether it be the Boot Sequencer or another component. The SOC can utilize the breakpoint and DMI capability to halt the Boot Sequencer before bring up the MCU and do targeted register accesses via the DMI port which is connected to the MCU.
MCI Block Diagram:

Parameters & Defines
Note: Additional parameter limitations can be seen in the Requirements section
Table: AXI Integration Parameters
| Facing | Parameter name | Location | Description |
|---|---|---|---|
| External | AXI_ADDR_WIDTH | mci_top | AXI address width |
| External | AXI_DATA_WIDTH | mci_top | AXI data width |
| External | AXI_USER_WIDTH | mci_top | AXI user width |
| External | AXI_ID_WIDTH | mci_top | AXI ID width |
Table: MCU SRAM Integration Parameters
| Facing | Parameter name | Location | Description |
|---|---|---|---|
| External | MCU_SRAM_SIZE_KB | mci_top | Size of MCU SRAM in KB. i.e. Min: 4 Max: 2048(2MB) |
Table: MCI Boot Sequencer Integration Parameters
| Facing | Parameter name | Location | Description |
|---|---|---|---|
| External | MIN_MCU_RST_COUNTER_WIDTH | mci_top | Size of MCU reset counter which determines the min reset time for the MCU. When the timer overflows MCU can be brought up. |
Table: MCI MBOX Integration Parameters
| Facing | Parameter name | Location | Description |
|---|---|---|---|
| External | MCU_SET_MBOX0_AXI_USER_INTEG | mci_top | Determines if VALID_AXI_USER will be used by MCI |
| External | MCU_MBOX0_VALID_AXI_USER | mci_top | MBOX0 AXI user list enabled by SET_MBOX0_AXI_USER_INTEG |
| External | MCU_MBOX0_SIZE_KB | mci_top | Size of MBOX0 SRAM. If set to 0 the entire MBOX0 is removed from MCI. Min: 0 Max: 2048 (2MB) |
| External | MCU_SET_MBOX1_AXI_USER_INTEG | mci_top | Determines if VALID_AXI_USER will be used by MCI |
| External | MCU_MBOX1_VALID_AXI_USER | mci_top | MBOX1 AXI user list enabled by SET_MBOX0_AXI_USER_INTEG |
| External | MCU_MBOX1_SIZE_KB | external | Size of MBOX1 SRAM. If set to 0 the entire MBOX1 is removed from MCI. Min: 0 Max: 2048 (2MB) |
Table: MCI Integration Definitions
| Defines | Defines file | Description |
|---|---|---|
| NONE DEFINED |
Interface
Note: Additional interface connection requirements/guidance can be seen in the Requirements section
Note: Any port listed as βSTATICβ must be stable before mci_pwrgood is asserted. If the signal changes value after mci_pwrgood assertion will cause functional issues in MCI
Note: Internal means the signal is not directly exposed to the SOC. External means it is directly exposed for SOC to consume and connect.
Note: If a signal (like the clock) is combined with other IPs it is still listed as Ext.
Note: If a signal stays in the SS but will need SOC connection (AXI interfaces) due to the SS not instantiating a component (like an AXI interconnect) it is listed as Ext because the SOC will need to connect.
Note: Any port with known internal and external connections (i.e. agg_error_fatal) will have External/Internal with note in a different section on which ports are reserved for internal vs external use.
Table: MCI Clocks
| Facing | Type | Width | Name | Clock | Description |
|---|---|---|---|---|---|
| External | Input | 1 | clk | MCI Clock. Connected to subsystem top level clk input. | |
| External | Output | 1 | mcu_clk_cg | MCU clock gated when MCU in reset for RDC. Exposed as cptra_ss_mcu_clk_cg_o externally. | |
| External | Output | 1 | cptra_ss_rdc_clk_cg | MCI SS clock gated when caliptra reset asserted for RDC. Should be used whenever their is a Warm reset -> cold reset crossing in design. Must be paired with cptra_ss_rst_b_o reset for proper gating. Exposed to SOC as cptra_ss_rdc_clk_cg_o |
Table: MCI Resets
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| External | Input | 1 | mci_pwrgood | Active high power good indicator. Deepest reset domain for MCI. |
| External | Input | 1 | mci_rst_b | Active low asynchronous reset for MCI. |
| External | Input | 1 | cptra_ss_rst_b_o | Active low asyn reset for Caliptra SS. Delayed version of mci_rst_b in order to gate cptra_ss_rdc_clk_cg and mcu_clk_cg before reset assertions for RDC purposes. When scan_mode is set, this is directly controlle by mci_rst_b |
| External | Output | 1 | mcu_rst_b | Reset for MCU. When scan_mode is set, this is directly controlled by mci_rst_b. Exposed as cptra_ss_mcu_rst_b_o externally. |
| External | Output | 1 | cptra_rst_b | Reset for Caliptra. When scan_mode is set, this is directly controlled by mci_rst_b. Exposed as cptra_ss_mci_cptra_rst_b_o externally. |
Table: MCI AXI Interface
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| External | interface | s_axi_w_if | AXI subordinate write interface. Exposed to SOC as cptra_ss_mci_s_axi_if_w_sub | |
| External | interface | s_axi_r_if | AXI subordinate read interface. Exposed to SOC as cptra_ss_mci_s_axi_if_r_sub |
Table: MCI Straps
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| External | Input | AXI_USER_WIDTH | strap_mcu_lsu_axi_user | AXI USER for MCUβs load/store unit. Exposed to SOC via cptra_ss_strap_mcu_lsu_axi_user_i |
| External | Input | AXI_USER_WIDTH | strap_mcu_ifu_axi_user | AXI USER for MCUβs instruction fetch unit. Exposed to SOC via cptra_ss_strap_mcu_ifu_axi_user_i |
| External | Input | AXI_USER_WIDTH | strap_mcu_sram_config_axi_user | AXI USER populating MCU FW Image in MCU SRAM. Exposed to SOC via cptra_ss_strap_mcu_sram_config_axi_user_i |
| External | Input | AXI_USER_WIDTH | strap_mci_soc_config_axi_user | AXI USER with MCU privilages in MCI reg. Use for Romless config. 0x0: Disable 0xFFFFFFFF: Debug (all AXI users get this privilage). Exposed to SOC via cptra_ss_strap_mci_soc_config_axi_user_i |
| External | Input | 32 | strap_mcu_reset_vector | Default reset vector for MCI. Can be overridden via MCI register write. Exposed to SOC via cptra_ss_strap_mcu_reset_vector_i |
| External | Input | 32 | ss_debug_intent | Debug intent |
Table: MCI CG Controls
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| External | output | 1 | rdc_clk_dis | Clock disable for warm reset. Used to disable cptra_ss_rdc_clk_cg_o and cptra_ss_mcu_clk_cg_o clocks. Asserted a few clock cycles after cptra_ss_rst_b_i asserted and before cptra_ss_rst_b_o asserted. Deasserted a few clock cycles after cptra_ss_rst_b_i deasserted. Exposed as cptra_ss_warm_reset_rdc_clk_dis_o externally. |
| External | output | 1 | early_warm_reset_warn | Early reset warn used to change security related signals to a safe value before reset is asserted. Needed since Caliptra Core clock gating is slightly after MCI clock gating/reset assertion. Example is the security_state fed from MCI to Caliptra Core. Exposed as cptra_ss_early_warm_reset_warn_o externally |
| External | output | 1 | fw_update_rdc_clk_dis | Clock disable for MCU reset. Used to disable cptra_ss_mcu_clk_cg_o clock. Asserted a few clock cycles before cptra_ss_mcu_rst_b_o is asserted. Deasserted when MCI boot sequencer switches out of BOOT_RST_MCU state. Exposed as cptra_ss_mcu_fw_update_rdc_clk_dis_o externally |
Table: MCI MISC Interface
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| Internal/External | input | 1 | mcu_sram_fw_exec_region_lock | FW_EXEC_CTRL[2] from Caliptra or SOC to indicate if there is a new MCU FW update avaialbe. Exposed to SOC as cptra_ss_cptra_generic_fw_exec_ctrl_2_mcu_i |
| External | input | 64 | mci_generic_input_wires | Generic input wires SOC can use to interrupt MCU. Exposed to SOC as cptra_ss_mci_generic_input_wires_i |
| External | output | 64 | mci_generic_output_wires | Generic output wires MCU can use to control SOC logic. Exposed to SOC as cptra_ss_mci_generic_output_wires_o |
| External | input | 1 | mcu_no_rom_config | 1: No rom config enabled 0: No rom config. Exposed to SOC as cptra_ss_mcu_no_rom_config_i |
Table: MCI MCU Interface
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| Internal | output | 32 | mcu_reset_vector | MCU reset vector |
| Internal | output | 1 | mcu_cpu_halt_req_o | MCU halt request, used by MCI boot FSM. Exposed as ouput to SOC as cptra_ss_mcu_halt_req_o |
| Internal | input | 1 | mcu_cpu_halt_ack_i | MCU halt ack, used by MCI boot FSM. Exposed as output to SOC as cptra_ss_mcu_halt_ack_i |
| External/Internal | input | 1 | mcu_cpu_halt_status_i | MCU halt status, used by MCI boot FSM, and exposed as input to SOC as cptra_ss_mcu_halt_status_i |
| Internal | output | 1 | mcu_dmi_core_enable | MCU DMI Core Enable |
| Internal | output | 1 | mcu_dmi_uncore_enable | MCU DMI Uncore Enable |
| Internal | input | 1 | mcu_dmi_uncore_en | MCU DMI Uncore Interface Enable |
| Internal | input | 1 | mcu_dmi_uncore_wr_en | MCU DMI Uncore Interface Write Enable |
| Internal | input | 7 | mcu_dmi_uncore_addr | MCU DMI Uncore Interface Address |
| Internal | input | 32 | mcu_dmi_uncore_wdata | MCU DMI Uncore Interface Write Data |
| Internal | input | 32 | mcu_dmi_uncore_rdata | MCU DMI Uncore Interface Read Data |
| Internal | input | 1 | mcu_dmi_active | MCU DMI Interface Active |
| Internal | input | 1 | mcu_dmi_active | MCU DMI Interface Active |
| Internal | input | 32 | mcu_trace_rv_i_insn_ip | MCU trace instruction |
| Internal | input | 32 | mcu_trace_rv_i_address_ip | MCU trace address |
| Internal | input | 1 | mcu_trace_rv_i_valid_ip | MCU trace valid |
| Internal | input | 1 | mcu_trace_rv_i_exception_ip | MCU trace exception |
| Internal | input | 5 | mcu_trace_rv_i_ecause_ip | MCU trace exception cause |
| Internal | input | 1 | mcu_trace_rv_i_interrupt_ip | MCU trace interrupt |
| Internal | input | 32 | mcu_trace_rv_i_tval_ip | MCU trace exception trap value |
Table: MCI Errors and Interrupts Interface
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| Internal/External | input | 32 | agg_error_fatal | Fatal errors from other Caliptra SS IPs or other SOC entities fed into MCIβs aggregate error infrastructure and will be reflected for SOC consumption via the all_error_fatal output wire of MCI |
| Internal/External | input | 32 | agg_error_non_fatal | Non-fatal errors from other Caliptra SS IPs or other SOC entities fed into MCIβs aggregate error infrastructure and will be reflected for SOC consumption via the all_error_non_fatal output wire of MCI. |
| External | Output | 1 | all_error_fatal | Fatal error interrupt for SOC consumption. Exposed as cptra_ss_all_error_fatal_o to SOC |
| External | Output | 1 | all_error_non_fatal | Non-fatal error interrupt for SOC consumption. Exposed as cptra_ss_all_error_non_fatal_o to SOC |
| Internal | Output | 1 | mcu_timer_int | MCUβs standard RISC-V MTIMER interrupt. |
| Internal | Output | 1 | mci_intr | MCI interrupt indication for MCU. This will be set when an unmasked interrupt occurs within MCI. This is a level interrupt and must be cleared by MCU firmware. |
| Internal | Output | 1 | nmi_intr | Non-maskable interrupt for MCU. This is connected to the watchdog (WDT) timer within MCI and will be asserted when the wdt is in cascade mode and both timers timeout. It can only be cleared by asserting mci_rst_b. This interrupt is also fed into the all_error_fatal infrastructure for SOC consumption. |
| Internal | Output | 32 | mci_nmi_vector | Non-maskable interrupt vector for MCU. This is controllable only by MCU FW. |
| Internal | input | 1 | cptra_mbox_data_avail | Cptra mailbox data is available for SOC/MCU. Fed into MCI interrupts for MCU. |
| Internal | input | 1 | intr_otp_operation_done | FC OTP operation done interrupt fed into MCI interrupts for MCU. |
| Internal | output | 1 | soc_mcu_mbox0_data_avail | MCU MBOX0 data available for SOC. |
| Internal | output | 1 | soc_mcu_mbox1_data_avail | MCU MBOX0 data available for SOC. |
Table: MCI LCC Bring Up Interface
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| Internal | Input | 1 | lc_done | LCC initialization done response used by MCU boot sequencer to move to the next state. |
| Internal | Output | 1 | lc_init | LCC initialization request asserted by the MCU boot sequencer after every MCI reset. |
Table: MCI FC Bring Up Interface
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| Internal | Input | 1 | fc_opt_done | FC initialization done response used by MCU boot sequencer to move to the next state. |
| Internal | Output | 1 | fc_opt_init | FC initialization request asserted by the MCU boot sequencer after every MCI reset. |
Table: MCU SRAM Interface
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| External | interface | mci_mcu_sram_req_if | Data width is DATA+ECC. Address width shall be wide enough to address entire SRAM. | MCU SRAM memory interface. |
| External | interface | mci_mbox0_sram_req_if | Data width is DATA+ECC. Address width shall be wide enough to address entire SRAM. | MBOX0 SRAM memory interface. |
| External | interface | mci_mbox1_sram_req_if | Data width is DATA+ECC. Address width shall be wide enough to address entire SRAM. | MBOX1 SRAM memory interface. |
Table: MCI LCC Gasket Interface
| Facing | Type | Width | Name | Description |
|---|---|---|---|---|
| Internal | Input | Struct | from_lcc_to_otp_program_i | These are the LCC port lists that program corressponding fuse partition for LCC |
| Internal | Input | Struct | lc_dft_en_i | LCC decoding signal, see LCC section |
| Internal | Input | Struct | lc_hw_debug_en_i | LCC decoding signal, see LCC section |
| Internal | Input | Struct | from_otp_to_lcc_program_i | These ports comes from fuse partitions and show LCC's non-volatile state |
| Internal | Input | 1 | ss_dbg_manuf_enable_i | Caliptra Core enables manuf debug with this |
| Internal | Input | 64 | ss_soc_dbg_unlock_level_i | Caliptra Core enables prod debug with this. Since there are multiple debug levels, the debug level is one-hot encoded to this port |
| External | Output | 1 | SOC_DFT_EN | Masked LCC decoding signal, see LCC section |
| External | Output | 1 | SOC_HW_DEBUG_EN | Masked LCC decoding signal, see LCC section |
| Internal | Output | Struct | security_state_o | Caliptra Core's security state |
| External | Input | 1 | FIPS_ZEROIZATION_PPD_i | Physical pin to trigger zeroization |
| Internal | Output | 1 | FIPS_ZEROIZATION_CMD_o | Masked zeroization command signal |
Memory Map / Address map
Top Level Memory Map
| Internal Block | Address Offset (from base address) | End Address |
|---|---|---|
| CSRs | 0x0 | 0x1FFF |
| MCU Trace Buffer | 0x10000 | 0x1001F |
| Mailbox 0 | 0x400000 | 0x7FFFFF |
| Mailbox 1 | 0x800000 | 0xBFFFFF |
| MCU SRAM | 0xC00000 | MCU SRAM BASE + MCU_SRAM_SIZE |
MCU SRAM Memory Map
MCU SRAM is split into 2 sections:
- Updatable Execution Region
- Protected Data Region
The two regions have different access protection. The size of the regions is dynamically changed via the FW_SRAM_EXEC_REGION_SIZE register in 4KB increments.
Table: MCU SRAM Regions
| MCU SRAM Region | Address Start Offset |
|---|---|
| Updatable Execution Region | 0x0 |
| Protected Region | FW_SRAM_EXEC_REGION_SIZE * 1024 * 4 |
NOTE: FW_SRAM_EXEC_REGION_SIZE is base 0 meaning the minimum size for the Updatable Execution Region is 4KB.
NOTE: If FW_SRAM_EXEC_REGION_SIZE is the size of the SRAM, there is no protected Region.
MCI Integration Requirements
-
Connectivity, Clock & Reset, Constraints & Violations etc
-
AXI Parameters and AXI Interface Parameter Alignment
Due to SystemVerilog limitations MCI exposed both AXI parameters and AXI interfaces that are parameterized. Parameters between all AXI interface and the MCI AXI parameters must be consistent.
-
AXI DATA_WIDTH Limitation
MCI DATA_WIDTH was only verified with a value of 32. If a different width is required the user will have to make internal MCI changes and no functionality is guaranteed.
-
AXI ADDR_WIDTH Limitation
AXI ADDR_WIDTH must be wide enough to fully address the MCI address space.
-
MCI Base Address Requirement
The base address assigned to MCI must align to the MCI total addressable space. This can be calculated based off of the MCU SRAM size since it is the last block in the MCI memory map.
To calculate the base address alignment use the following calculation:
bits = $clog2(MCU_SRAM_OFFSET + ((MCU_SRAM_SIZE_KB * 1024) - 1))MCU_SRAM_OFFSET can be found in the MCIβs Top Level Memory Map.
Example:
MCU_SRAM_OFFSET = 0xC00000 (12582912 decimal) MCU_SRAM_SIZE_KB = 512 (512KB) bits = $clog2(12582912 + ((512 * 1024) - 1)) bits = 24 MCI base address would need to align to 0x80_0000 -
Reset Synchronization
MCI does not synchronize any resets inside the IP. It is expected all reset inputs are properly synchronized to the MCI clock domain before being passed to MCI.
All MCI reset outputs are synchronous to the MCI clock domain and if they are used in a different clock domain they shall be properly synchronized outside of MCI.
-
DFT Reset Control
MCI input resets do not have any built-in DFT reset control for scan. It is the integratorβs responsibility to add any DFT controls outside of MCI before the reset is connected to MCI.
Simlar to Caliptra core - When
scan_modeis set the MCI generated resets will be directly controlled bymci_rst_b. This gives DFT complete control of these resets within Caliptra SS. -
Integrator RTL modification requirements
MCI reused synchronizer modules from Caliptra Core like caliptra_2ff_syn.sv. Integrators are required to replace these modules with technology-specific sync cells.
MCI does not itself contain modules that need to be directly modified by the integrator.
-
Late Binding interface signals
The interface signals
mci_generic_input_wiresandmci_generic_output_wiresare placeholders on the SoC interface reserved for late binding features. This may include any feature that is required for correct operation of the design in the final integrated SoC and that may not be accommodated through existing interface signaling (such as the mailbox).Each wire connects to a register in the MCI register bank through which communication to the MCU may be facilitated. Each of the generic wire signals is 64 bits in size.These signals are considered ASYNC and each of the 64 bits are considered separate adhoc signals. Meaning there is no bus synchronization which means the connections to this port need to be thoroughly thought through to ensure the MCU doesnβt drop any requests.
Activity on any bit of the
mci_generic_input_wirestriggers a notification interrupt to the microcontroller indicating a bit toggle.The following tables describe the allocation of functionality on
mci_generic_input_wiresandmci_generic_output_wires. Bits not assigned to a function can be used by the SOC for their own needs. These generic wires could be reserved by CHIPS Alliance in future Caliptra drops. Any unused inputs shall be tied off to 0 and outputs left unconnected.Table: MCI Generic Input Allocation
-
| Bits | Name | Description |
|---|---|---|
| 63:0 | RESERVED | No allocated function |
**Table: MCI Generic Output Allocation**
| Bits | Name | Description |
|---|---|---|
| 63:0 | RESERVED | No allocated function |
Error Aggregation Connectivity Requirements
MCI aggregates all fatal and non-fatal errors for Caliptra SS via two ports agg_error_fatal and agg_error_non_fatal. These errors are:
- Sent to MCU via interrupt
- Sent to SOC via
all_error_fatalorall_error_non_fatalMCI output ports
Errors connected to this infrastructure are required to be level signals. Pulses are not permitted.
MCU has the ability to independently mask these aggregated interrupts to its own interrupt and to the all_error_fatal and all_error_non_fatal output ports.
Aggregate error connections can be see in Caliptra SS HW Spec: MCI Error Handling.
Subsystem Internal Fuse Controller Initialization Connectivity Requirements
During Caliptra SS bring up the MCI handshakes with the FC to do initialization. The flow is:
- MCI: Assert lc_init
- FC: Assert lc_done
Connections between MCI and FC are shown in the table below:
Table: MCI to FC Init Connections
| MCI Port | FC Port |
|---|---|
| fc_opt_init | pwr_otp_i.otp_init |
| fc_opt_done | pwr_otp_o.otp_done |
Subsystem Internal Life Cycle Controller Initialization Connectivity Requirements
During Caliptra SS bring up the MCI handshakes with the LCC to do initialization. The flow is:
- MCI: Assert fc_opt_init
- FC: Assert fc_opt_done
Connections between MCI and LCC are shown in the table below:
Table: MCI to LCC Init Connections
| MCI Port | FC Port |
|---|---|
| lc_init | pwr_lc_i.lc_init |
| lc_done | pwr_lc_o.lc_done |
MCI MCU Connectivity Requirements
The table below shows connections between MCI and MCU that are not part of other features:
Table: MCI to MCU Connections
| MCI Port | Direction | MCU Port | Description |
|---|---|---|---|
| mcu_reset_vector | -> | rst_vec | Reset vector for MCU |
| nmi_intr | -> | nmi_intr | WDT interrupt for MCU |
| mcu_nmi_vector | -> | nmi_vec | MCU nonmaskable interrupt vector |
| mcu_rst_b | -> | rst_l | MCU reset |
MCI Caliptra Core Connectivity Requirements
The table below shows connections between MCI and Caliptra Core that are not part of other features:
Table: MCI to Caliptra Core Connections
| MCI Port | Direction | Caliptra Port | Description |
|---|---|---|---|
| mcu_sram_fw_exec_region_lock | <- | ss_generic_fw_exec_ctrl[2] | Controls MCU SRAM protection and used to bring MCU into reset for hitless match. NOTE: connection is looped by SOC at top level allowing SOC to use their own register to control this input. This is needed if Caliptra is not used. |
| cptra_rst_b | -> | cptra_rst_b | Reset for Caliptra. NOTE: Connection is looped by SOC at top level allowing for SOC to keep Caliptra in reset if not needed. |
LCC Gasket Connectivity Requirements
Below are the connections needed between MCI and LCC for the Gasket functionality
Table: LCC Gasket - MCI to LCC Connections
| MCI Port | Direction | LCC Port | Description |
|---|---|---|---|
| from_lcc_to_otp_program_i | <- | lc_otp_program_o | See LCC Section |
| lc_dft_en_i | <- | lc_dft_en_o | See LCC Section |
| lc_hw_debug_en_i | <- | lc_hw_debug_en_o | See LCC Section |
| from_otp_to_lcc_program_i | <- | otp_lc_data_i | See LCC Section |
Table: LCC Gasket - MCI to Caliptra Core Connections
| MCI Port | Direction | Caliptra Core Port | Description |
|---|---|---|---|
| ss_dbg_manuf_enable_i | <- | ss_dbg_manuf_enable | See Caliptra Integration spec |
| ss_soc_dbg_unlock_level_i | <- | ss_soc_dbg_unlock_level | See Caliptra Integration spec |
| security_state_o | -> | security_state | See LCC state tranlation table |
Table: LCC Gasket - MCI to Caliptra SS Port Connections
| MCI Port | Direction | SS Port | Description |
|---|---|---|---|
| SOC_DFT_EN | -> | cptra_ss_soc_dft_en_o | SOC DFT enable see DFT LC States |
| SOC_HW_DEBUG_EN | -> | cptra_ss_soc_hw_debug_en_o | SOC HW Debug Enable see: DFD LC States |
Table: Fuse Zeroization Signals - Caliptra SS Port Connections to MCI to FC
| MCI Port | Direction | SS Port | FC Port | Description |
|---|---|---|---|---|
| FIPS_ZEROIZATION_PPD_i | <- | cptra_ss_FIPS_ZEROIZATION_PPD_i | - | Zeroization enable see Zeroization Process |
| FIPS_ZEROIZATION_CMD_o | -> | - | FIPS_ZEROIZATION_CMD_i | Zeroization enable see Zeroization Process |
MCU SRAM Sizing Requirements
MCU SRAM size is set via the MCU_SRAM_SIZE_KB parameter.
The minimum size supported is 4KBs.
The maximum size supported is 2MBs.
It is suggested the size is on a 4KB boundary since the split between the execution region and secure region is done via MCI CSR in 4KB increments.
Standard size is 512KB.
The size of the SRAM should be determined based on:
- MCU runtime image size
- STACK
- Large data structures or persistent logs maintained by MCU
- .data/.bss sections
- Any other data MCU plans on storing the the MCU SRAM.
Storage guidance for Execution vs Protected region are as follows:
- Execution
- Executable Runtime Instructions for MCU
- .data/.bss sections
- STACK
- Protected
- STACK
- Incorruptible data structures
- Persistent logs maintained by MCU
Access to MCU SRAM Execution Region is controlled by mcu_sram_fw_exec_region_lock. Before MCU is brought out of reset for a hitless patch this signal must be asserted giving access to the MCU. Failure to do so will trigger an AXI access error since Caliptra will be the only entity allowed to access the SRAM until this signal is asserted.
MCU MBOX SRAM Sizing Requirements
MCU MBOX SRAM size is set via MCU_MBOX0_SIZE_KB and MCU_MBOX1_SIZE_KB parameters
The MCU can be configured to implement up to 2 independent mailboxes with maximum fully addressable SRAM. sizes of up to 2 MB. If they are configured sizes of 0, they will not be instantiated.
Size should be determined by:
- Max MCU FW image size
- Max size of other FW images Caliptra SS will handle
Programming interface
MCI Register Spec
MCU Mailbox
Each mailbox can be used for secure and restricted communication between external SoC entities, the MCU, and Caliptra core. This communication channel is essential for exchanging control messages, status updates, and other critical information that the MCU will use to monitor system boot, firmware updates, and security critical operations. Mailboxes are designed to ensure that only authorized entities can access and communicate through it, preserving the integrity and security of the SoC operations.
The mailboxes are generic with some specific protocols and legal operations/accesses to ensure their secured and restricted aspect. The command, statuses, and data that are intepreted by the MCU microcontroller (uC), Caliptra uC or other SoC agent are not defined or enforced in this specification.
Since the MCU uC has root access to the mailboxes (even when the mailbox is locked), it can function as an intermediary and facilitate communication between 2 agents (such as Caliptra uC and other SoC agents).
The Caliptra SS HW MCU Mailbox Spec has more details about the specific registers and interrupts used in the mailbox flows.
MCU Mailbox Limited Trusted AXI users
If build-time integration straps are not used for configuring the trusted MBOX AXI users, then trusted users will need to configured with the following lockable MCI registers before MBOX can be used by SOC agents:
MBOX*_VALID_AXI_USERMBOX*_AXI_USER_LOCK
See Caliptra SS MCU Trusted AXI Users for more details.
Reset
The mailboxes start locked by MCU to prevent any data leaks across warm reset. MCU shall set MBOX_DLEN to MBOX SRAM size and write 0 to MBOX_EXECUTE to release the MBOX and wipe the MBOX SRAM. This should be done before using or allowing use of the mailboxes.
MCU Mailbox Doorbell Command DLEN
An MBOX doorbell command has no data. When MBOX_DLEN = 0 and MBOX_EXECUTION is cleared, the clearing logic erases the entire MBOX SRAM. To avoid long delays caused by this clearing, firmware should set MBOX_DLEN = 1 when issuing doorbell commands.
MCI Debug Lock Status
MCI exposed the debug state of the chip to MCU via an interrupt notif_debug_locked_sts and a status register SECURITY_STATE.debug_locked. There is no defined use case for this feature at subsystem level, unlike in Caliptra where security assets are cleared when entering debug locked state. Therefore, it is recommended this interrupt remains disabled using notif_debug_locked_en.
If there is a need to use debug state the recommened flow to avoid missing a debug locked transition is:
- Out of reset MCU W1C
notif_debug_locked_sts - MCU reads
SECURITY_STATE.debug_lockedand acts on the value seen - MCU enables the interrupt using
notif_debug_locked_en
MCU to SOC Receiver Flow
- MCU attempts to lock mailbox by reading
MBOX_LOCKregister
- If read returns 0, LOCK is granted and will be set to 1
- If read returns 1, MBOX is locked for another agent
- MCU writes data to
MBOX_SRAM - MCU writes data length in bytes to
MBOX_DLEN - MCU writes command to
MBOX_CMDregister - MCU sets
MBOX_TARGET_USERto SOC Receiver AXI and sets MBOX_TARGET_USER_VALID to 1 - MCU writes 1 to
MBOX_EXECUTEregister (which asserts cptra_ss_soc_mcu_mbox*_data_avail output) - MCU can directly inform receiver depending on receiver capabilities OR receiver could use cptra_ss_soc_mcu_mbox*_data_avail wire to directly generate an interrupt
- Receiver processes command and data in mailbox
- Receiver updates
MBOX_SRAMandMBOX_DLEN(if there is data to return). - Reciever updates status in
MBOX_TARGET_STATUS.STATUSand setsMBOX_TARGET_STATUS.DONE
- This generates interrupt MBOX*_TARGET_DONE to MCU
- MCU (in response to MBOX*_TARGET_DONE interrupt) will write 0 to
MBOX_EXECUTEto release the MBOX - MCI clears MBOX CSRs and zeros out data from 0 to the max DLEN set during the whole lock session in
MBOX_SRAM
- Mailbox lock cannot be re-acquired until zeroization is complete
SOC Sender to MCU Flow
- Sender attempts to lock mailbox by reading
MBOX_LOCKregister
- If read returns 0, LOCK is granted and will be set to 1
- If read returns 1, MBOX is locked for another agent
- Sender writes data to
MBOX_SRAM - Sender writes data length in bytes to
MBOX_DLEN - Sender writes command to
MBOX_CMDregister - Sender writes 1 to
MBOX_EXECUTEregister
- This generates MBOX*_CMD_AVAIL interrupt to MCU
- MCU processes command and data in mailbox
- MCU updates
MBOX_SRAMandMBOX_DLEN(if there is data to return). - MCU update
MBOX_CMD_STATUSwith desired status code - Sender writes 0 to
MBOX_EXECUTEto release the MBOX - MCI clears MBOX CSRs and zeros out data from 0 to the max DLEN set during the whole lock session in
MBOX_SRAM
- Mailbox lock cannot be re-acquired until zeroization is complete
SOC Sender to SOC Receiver Communication Flow (MCU as intermediary)
- Sender attempts to lock mailbox by reading
MBOX_LOCKregister
- If read returns 0, LOCK is granted and will be set to 1
- If read returns 1, MBOX is locked for another agent
- Sender writes data to
MBOX_SRAM - Sender writes data length in bytes to MBOX_DLEN
- Sender writes command to
MBOX_CMDregister - Sender writes 1 to
MBOX_EXECUTEregister
- This generates MBOX*_CMD_AVAIL interrupt to MCU
- Sender reads/polls
MBOX_CMD_STATUSfor desired completion/ready status - MCU (in response to MBOX*_CMD_AVAIL interrupt) processes command and data
- MCU sets
MBOX_TARGET_USERto SOC Receiver AXI and sets MBOX_TARGET_USER_VALID to 1 - MCU notifies SOC Receiver they can access MBOX (method depends on SOC Receiver capabilities)
- Receiver reads MBOX and processes command and/or data
- Receiver potentially updates data in
MBOX_SRAMandMBOX_DLEN - Reciever updates status in
MBOX_TARGET_STATUS.STATUSand setsMBOX_TARGET_STATUS.DONE
- This generates interrupt MBOX*_TARGET_DONE to MCU
- MCU (in response to MBOX*_TARGET_DONE interrupt) will update
MBOX_CMD_STATUSwith final status - Sender sees final desired
MBOX_CMD_STATUS - Sender writes 0 to
MBOX_EXECUTEto release the MBOX - MCI clears MBOX CSRs and zeros out data from 0 to the max DLEN set during the whole lock session in
MBOX_SRAM
- Mailbox lock cannot be re-acquired until zeroization is complete
MCU JTAG/DMI Access

MCI provides access JTAG security for MCU. Detailed debug architecture can be found in MCI debug spec. Some notes about MCI debug arcitecture:
- MCI registers are accessed via the MCU DMI uncore address space.
- Limited MCU JTAG access is provided to MCI DMI registers by setting
cptra_ss_debug_intent_i - Full access to MCU core and uncore DMI registers is restricted to LCC unlocked mode.
MCU SRAM JTAG Access
MCU SRAM is fully accessable via MCU JTAG. MCU must be halted before accessing MCU SRAM via JTAG otherwise collisions between AXI and JTAG will result in errors and corrupted data.
MCU Trace Buffer

MCI hosts the MCU trace buffer. The full trace buffer spec is here. High level notes about trace buffer:
- It can only be accessed when debug unlocked
- It can be accessed via AXI or DMI
- Traces can be disabled via a RISC-V control register
- Traces are sticky and persist through warm reset
- The trace buffer can hold a total of 64 traces
MCU Halt Ack Interface
Caliptra SS exposed the MCU halt ack handshake to enable MCU No Rom Config.
If the SOC does not support MCU No Rom Config Caliptra SS requires the signals to be looped back.
| Caliptra SS port | Direction | Connection |
|---|---|---|
cptra_ss_mcu_halt_status_o | output | cptra_ss_mcu_halt_status_i |
cptra_ss_mcu_halt_status_i | input | cptra_ss_mcu_halt_status_o |
cptra_ss_mcu_halt_ack_o | output | cptra_ss_mcu_halt_ack_i |
cptra_ss_mcu_halt_ack_i | input | cptra_ss_mcu_halt_ack_o |
cptra_ss_mcu_halt_req_o | output | Unused by SOC |
If the SOC supports MCU No Rom Config the SOC must drive the halt_ack/status during the first Caliptra SS boot out of Cold Reset since MCU is still in reset. It shall give control back to MCU after it has completed the initial halt handshake as specified in MCU No ROM Config flow. The recommended connections if MCU No Rom Config is supported:
| Caliptra SS port | Direction | Connection |
|---|---|---|
cptra_ss_mcu_halt_status_o | output | cptra_ss_mcu_halt_status_i |
cptra_ss_mcu_halt_status_i | input | cptra_ss_mcu_halt_status_o ORed with SOC control |
cptra_ss_mcu_halt_ack_o | output | cptra_ss_mcu_halt_ack_i |
cptra_ss_mcu_halt_ack_i | input | cptra_ss_mcu_halt_ack_o ORed with SOC control |
cptra_ss_mcu_halt_req_o | output | Used to enable/disable the SOC control of ack/status |
Sequences : Reset, Boot,
MCI Boot Sequencer

The MCI is responsible for bringing up the Caliptra SS. This is done via the MCI Boot Sequencer. The primary job of this FSM is to:
- Initialize the fuse controller
- Initialize the life cycle controller
- Allow a breakpoint for debug intervention before MCU or Caliptra are out of reset
- Bring MCU out of reset
- Bring Caliptra out of reset
- Control MCU reset for hitless updates
Breakpoint Flow
The SOC can halt the MCI Boot Sequencer via the mcu_boot_seq_brkpoint port. When set to 1 it will cause the MCI Boot Sequence to halt after it has initialized both FC and LCC.
This port shall be set and stable before mcu_rst_b is deasserted. Failure to do so will mean the breakpoint might be missed by MCI Boot Sequencer.
Once in BOOT_BREAKPOINT a user can use MCU JTAG to configure MCI other components within the Caliptra SS for debug.
One known usecase for the breakpoint is to bypass the ROM and jump to a debug image in MCU SRAM.
To proceed after a breakpoint the SOC must write the MCI_BOOTFSM_GO.go register via AXI or MCI DMI port.
MCU No ROM Config
Caliptra SS supports booting without a MCU ROM. To enable this configuration the SOC must:
cptra_ss_mcu_no_rom_config_iset to 1cptra_ss_strap_mci_soc_config_axi_user_iset to a trusted user other than MCUcptra_ss_mcu_halt_status_iconnected tocptra_ss_mcu_halt_status_oORed with SOC controlcptra_ss_mcu_halt_ack_iconnected tocptra_ss_mcu_halt_ack_oORed with SOC control- (optional)
cptra_ss_strap_mcu_reset_vector_iset to known starting address in MCU SRAM
The expected boot sequence is:
- MCI brought out of reset
- MCI boot FSM progresses to
WAIT_FOR_CPTRA_BOOT_GO - Trusted SOC agent does configuration MCU ROM typically executes. See CSS HW spec
- Trusted SOC agent sets
CPTRA_BOOT_GO.gobringing Caliptra out of reset - Trusted SOC agent executes MCU FW Boot Update with Caliptra
- When SOC agent sees
notif_cptra_mcu_reset_req_stsset by Caliptra, SOC will seecptra_ss_mcu_halt_req_oasserted by MCI Boot FSM. SOC must assertcptra_ss_mcu_halt_status_iandcptra_ss_mcu_halt_ack_iback to MCI. When SOC seescptra_ss_mcu_halt_req_odeassert SOC shall give full control of these signals back to MCU. - See MCU Halt Ack Interface for recommended connections.
During step 5 MCU will be brought out of reset and start executing from MCU SRAM.
No Caliptra Core Config
If an SOC does not need Caliptra and wants to use a different entity to service MCU FW updates the SOC can:
- Tie
cptra_ss_rst_b_ito 0 - Leave
cptra_ss_rst_b_ounused - Set
cptra_ss_strap_mcu_sram_config_axi_user_ito a trusted user- Gives SOC agent access to MCU SRAM allowing it to populate it with a FW image.
cptra_ss_cptra_generic_fw_exec_ctrl_2_mcu_icontrolled by the trusted user instead of Caliptra- Gives SOC agent reset request control of MCU.
- Leave
cptra_ss_cptra_generic_fw_exec_ctrl_2_mcu_ounused
Connectivity requirements can be found in FW Execution Control Connections and Caliptra Core Reset Control
Reset Ordering
The following table defines the order in which resets can get asserted. A ">>" in a cell at row X and column Y indicates that if the reset in row X is asserted, the reset in row Y is also asserted. For the rest of the cells (in which symbol ">>" is not present) the preceding assumption is not true and so the paths between those resets are potential RDC violations. The "N/A" cells are ignored because they are between the same resets.
Table: MCI Reset Ordering
| mci_pwrgood | mci_rst_b | mcu_rst_b | cptra_rst_b | |
|---|---|---|---|---|
| mci_pwrgood | N/A | >> | >> | >> |
| mci_rst_b | N/A | >> | >> | |
| mcu_rst_b | N/A | |||
| cptra_rst_b | N/A |
MCU FW Update Flows
The hitless flow is described in full in Caliptra Top Spec. The Caliptra SS HW Spec spec gives details about the registers used in theese flow. This section is meant to elaborate on how to use the given HW to meet the architectual spec.
Registers relevant to these flows:
- Caliptra
SS_GENERIC_FW_EXEC_CTRL[0].go[2]
- MCI
RESET_STATUS.mcuMCU_RESET_REQRESET_REASONnotif_cptra_mcu_reset_req_sts
MCU FW Boot Update
First MCU FW Update after a Cold Reset.
- Out of Cold Boot Caliptra has access to MCU SRAM since
FW_EXEC_CTRL[2]is reset to 0. - MCU ROM should use
notif_cptra_mcu_reset_req_stsinterrupt to know when Caliptra has a FW image for MCU. MCU ROM can either poll or enable the interrupt. - Caliptra sets MCI's
RESET_REASON.FW_BOOT_UPD_RESETand Caliptra'sFW_EXEC_CTRL[2]indicating a FW image is ready for MCU in MCU SRAM. - MCU sees request from Caliptra and shall clear the interrupt status.
- MCU sets
RESET_REQUEST.mcu_reqin MCI to request a reset. - MCI does an MCU halt req/ack handshake to ensure the MCU is idle
- MCI asserts MCU reset (min reset time for MCU is until MIN_MCU_RST_COUNTER overflows)
- MCU is brought out of reset and checks MCI's
RESET_REASON - MCU jumps to MCU SRAM
MCU Hitless FW Update
Subsequent MCU FW Update after FW Boot Update.
- MCU FW should use
notif_cptra_mcu_reset_req_stsinterrupt to know when Caliptra has a FW image for MCU. MCU FW can either poll or enable the interrupt. - Caliptra clears
FW_EXEC_CTRL[2]indicating a FW image is ready for MCU. - MCU sees request from Caliptra and shall clear the interrupt status.
- MCU sets
RESET_REQUEST.mcu_reqin MCI to request a reset. - MCI does an MCU halt req/ack handshake to ensure the MCU is idle
- MCI asserts MCU reset (min reset time for MCU is until MIN_MCU_RST_COUNTER overflows)
- Caliptra will wait until
RESET_STATUS.MCU_RESET_STSis set to indicate that reset is complete. - Caliptra will then have access to MCU SRAM Updatable Execution Region and update the FW image.
- Caliptra sets
RESET_REASON.FW_HITLESS_UPD_RESET - Caliptra sets
FW_EXEC_CTRL[2] - MCU is brought out of reset and checks MCI's
RESET_REASON - MCU jumps to MCU SRAM
MCU Warm Reset FW Update
Caliptra SS reset toggle without powergood toggle.
IMPORTANT - Can only happen after both Caliptra Core and MCU have loaded mutable firmware. Otherwise only Cold Reset is allowed.
- MCU ROM comes out of reset and sees
WARM_RESET. It cannot jump to MCU SRAM since it is locked and needs Caliptra to unlock. - MCU ROM brings Caliptra out of reset
- Caliptra sees Warm Reset and starts executing from its ICCM (SRAM image)
- Caliptra clears MCI's
RESET_REASON.WARM_RESETand setsRESET_REASON.FW_BOOT_UPD_RESET - Caliptra sets
FW_EXEC_CTRL[2] - MCU sees request from Caliptra and shall clear the interrupt status.
- MCU sets
RESET_REQUEST.mcu_reqin MCI to request a reset. - MCI does an MCU halt req/ack handshake to ensure the MCU is idle
- MCI asserts MCU reset (min reset time for MCU is until MIN_MCU_RST_COUNTER overflows)
- MCU is brought out of reset and checks MCI's
RESET_REASON - MCU jumps to MCU SRAM
Error Flows
- Example all_error_fatal Flow
Below is an example flow an SOC can follow that would properly clear all interrupts for all_error_fatal:
Setup assumes all interrupts to MCU and all_error_fatal are enabled via MCI CSRs
- agg_error_fatal bit 0 is asserted by an IP
- error_agg_error_fatal0_sts for MCU will be asserted
- agg_error_fatal0 for SOC all_error_fatal will be asserted
- MCU:
- Interrupted via mci_intr
- Takes action on error
- This could just be a loop waiting for a reset as fatal errors typically need a system wide reset.
- Waits for interrupt source to be cleared see SOC steps
- W1C error_agg_error_fatal0_sts to clear the interrupt
- SOC:
- Interrupted via all_error_fatal
- Takes action on error
- Could be logging or resetting of the SOC
- Clears the source of the error causing agg_error_fatal[0] to be cleared
- W1C agg_error_fatal0
- At this point all interrupt registers within MCI register bank are cleared but all_error_fatal is still asserted.
- Reset MCI via mci_rst_b
- Clears the all_error_fatal output port
- Example all_error_non_fatal Flow
Below is an example flow an SOC can follow that would properly clear all interrupts for all_non_error_fatal:
Setup assumes all interrupts to MCU and all_error_non_fatal are enabled via MCI CSRs
- agg_error_fatal bit 0 is asserted by an IP
- notif_agg_error_fatal0_sts for MCU will be asserted
- agg_error_non_fatal0 for SOC all_error_non_fatal will be asserted
- MCU:
- Interrupted via mci_intr
- Takes action on error
- Could just be a logging of the non-fatal error
- Waits for interrupt source to be cleared see SOC steps
- W1C notif_agg_error_fatal0_sts to clear the interrupt
- SOC:
- Interrupted via all_error_non_fatal
- Takes action on error
- Could be logging or resetting of the SOC
- Clears the source of the error causing agg_error_non_fatal[0] to be cleared
- W1C agg_error_non_fatal0
- Once MCU and SOC have finished their flows all interrupts will be cleared
See MCI error handling for more details on MCI error infrastructure.
Other requirements
I3C core
Overview
The I3C core in the Caliptra Subsystem is an I3C target composed of two separate I3C targets:
- Main target : Main target is responsible for any flows other than recovery or streaming boot.
- Recovery target : Recovery target is dedicated to streaming boot / recovery interface.
The I3C core integrates with an AXI interconnect, allowing AXI read and write transactions to access I3C registers. For details on the coreβs internal registers and functionality, see:
The I3C core can be configured as an AXI Recovery interface. In this mode, the I3C endpoint is disabled as all internal FIFOs are repurposed for the recovery stream.
IMPORTANT:
- Static Configuration: The I3C core must be statically configured during the MCU boot flow as either an I3C Target or an AXI Recovery Interface. This selection is mutually exclusive and cannot be changed dynamically.
- Dual-Functionality: If the SoC requires both AXI Recovery and standard I3C Target functionality, a second I3C core must be instantiated outside of Caliptra SS.
Integration Considerations
- Connections
- Connection to AXI interface
- GPIO connection to I3C Host (VIP or RTL)
Parameters and defines
| Parameter/Define | Default Value | Description |
|---|---|---|
AxiDataWidth | `AXI_DATA_WIDTH | Width (in bits) of the AXI data bus |
AxiAddrWidth | `AXI_ADDR_WIDTH | Width (in bits) of the AXI address bus |
AxiUserWidth | `AXI_USER_WIDTH | Width (in bits) of the AXI user signals |
AxiIdWidth | `AXI_ID_WIDTH | Width (in bits) of the AXI ID signals |
DatAw | i3c_pkg::DatAw | Data address width (defined in i3c_pkg) |
DctAw | i3c_pkg::DctAw | Device context address width (i3c_pkg) |
CsrAddrWidth | I3CCSR_pkg::I3CCSR_MIN_ADDR_WIDTH | CSR address width (defined in I3CCSR_pkg) |
CsrDataWidth | I3CCSR_pkg::I3CCSR_DATA_WIDTH | CSR data width (defined in I3CCSR_pkg) |
DISABLE_INPUT_FF | Not Defined | DO NOT DEFINE. NO LONGER VALID CONFIG. If defined will remove synchronizer on SCL input signal causing CDC issue. |
Interface
| Signal | Direction | Width | Description |
|---|---|---|---|
clk_i | input | 1 bit | Clock input |
rst_ni | input | 1 bit | Active-low reset |
araddr_i | input | [AxiAddrWidth-1:0] | AXI read address |
arburst_i | input | 2 bits | AXI read burst type |
arsize_i | input | 3 bits | AXI read transfer size |
arlen_i | input | 8 bits | AXI read burst length |
aruser_i | input | [AxiUserWidth-1:0] | AXI read user signal |
arid_i | input | [AxiIdWidth-1:0] | AXI read transaction ID |
arlock_i | input | 1 bit | AXI read lock signal |
arvalid_i | input | 1 bit | AXI read address valid |
arready_o | output | 1 bit | AXI read address ready |
rdata_o | output | [AxiDataWidth-1:0] | AXI read data |
rresp_o | output | 2 bits | AXI read response |
rid_o | output | [AxiIdWidth-1:0] | AXI read transaction ID (response) |
rlast_o | output | 1 bit | AXI read last signal |
rvalid_o | output | 1 bit | AXI read valid |
rready_i | input | 1 bit | AXI read ready |
awaddr_i | input | [AxiAddrWidth-1:0] | AXI write address |
awburst_i | input | 2 bits | AXI write burst type |
awsize_i | input | 3 bits | AXI write transfer size |
awlen_i | input | 8 bits | AXI write burst length |
awuser_i | input | [AxiUserWidth-1:0] | AXI write user signal |
awid_i | input | [AxiIdWidth-1:0] | AXI write transaction ID |
awlock_i | input | 1 bit | AXI write lock signal |
awvalid_i | input | 1 bit | AXI write address valid |
awready_o | output | 1 bit | AXI write address ready |
wdata_i | input | [AxiDataWidth-1:0] | AXI write data |
wstrb_i | input | [AxiDataWidth/8-1:0] | AXI write strobe |
wlast_i | input | 1 bit | AXI write last |
wvalid_i | input | 1 bit | AXI write valid |
wready_o | output | 1 bit | AXI write ready |
bresp_o | output | 2 bits | AXI write response |
bid_o | output | [AxiIdWidth-1:0] | AXI write transaction ID (response) |
bvalid_o | output | 1 bit | AXI write response valid |
bready_i | input | 1 bit | AXI write response ready |
scl_i | input | 1 bit | I3C clock input |
sda_i | input | 1 bit | I3C data input |
scl_o | output | 1 bit | I3C clock output |
sda_o | output | 1 bit | I3C data output |
scl_oe | output | 1 bit | I3C clock output enable |
sda_oe | output | 1 bit | I3C data output enable |
sel_od_pp_o | output | 1 bit | Open-drain / push-pull selection (digital output) |
i3c_scl_io | inout | 1 bit (else) | I3C clock line (analog/digital) |
i3c_sda_io | inout | 1 bit (else) | I3C data line (analog/digital) |
recovery_payload_available_o | output | 1 bit | Indicates recovery payload is available and used by Caliptra Core. Exposed as cptra_ss_i3c_recovery_payload_available_o to SOC |
recovery_image_activated_o | output | 1 bit | Indicates the recovery image is activated and used by Caliptra Core. Exposed as cptra_ss_i3c_recovery_image_activated_o to SOC |
peripheral_reset_o | output | 1 bit | Resets connected peripherals |
peripheral_reset_done_i | input | 1 bit | Acknowledges peripheral reset completion |
escalated_reset_o | output | 1 bit | Escalated reset output |
irq_o | output | 1 bit | Interrupt request |
disable_id_filtering_i | input | 1 bit | Disables ID Filtering for I3C core |
priv_ids | input | 32 bit x NUM_PRIV_ID | 32 bit id list if ID filtering is enabled |
I3C Integration Requirements
- Connect the
cptra_ss_i3c_s_axi_ifwith AXI interconnect. - Follow the programming sequence described in Programming Sequence from AXI Side Point#1 to initialize the I3C targets.
- Follow the programming sequence described in Programming Sequence from AXI Side Point#2 to set both I3C target device with static addresses. Note, this is not required if I3C Host device is using the CCC
ENTDAAfor initializing the dynamic address to both targets. - If no external I3C connect
cptra_ss_i3c_recovery_image_activated_odirectly tocptra_ss_i3c_recovery_image_activated_i. If there is an external I3Ccptra_ss_i3c_recovery_image_activated_ocan be combined with or completely replaced with SOC logic and connected tocptra_ss_i3c_recovery_image_activated_i. - If no external I3C connect
cptra_ss_i3c_recovery_payload_available_odirectly tocptra_ss_i3c_recovery_payload_available_i. If there is an external I3Ccptra_ss_i3c_recovery_payload_available_ocan be combined with or completely replaced with SOC logic and connected tocptra_ss_i3c_recovery_payload_available_i.
Programming Sequence
Programming Sequence from AXI Side
-
Initialize the Device:
- Write to the necessary AXI registers to configure the I3C core.
- Ensure that the AXI interface is properly set up for communication.
- Refer to I3C register documentation at I3C Core Registers for more details.
// Reference Code to Initialize the I3C core (Target Mode) // This code initialized I3C core for default settings. uint32_t i3c_reg_data; i3c_reg_data = 0x00000000; i3c_reg_data = lsu_read_32(SOC_I3CCSR_I3C_EC_STDBYCTRLMODE_STBY_CR_CONTROL); i3c_reg_data = (2 << I3CCSR_I3C_EC_STDBYCTRLMODE_STBY_CR_CONTROL_STBY_CR_ENABLE_INIT_LOW) | i3c_reg_data; i3c_reg_data = (1 << I3CCSR_I3C_EC_STDBYCTRLMODE_STBY_CR_CONTROL_TARGET_XACT_ENABLE_LOW) | i3c_reg_data; lsu_write_32(SOC_I3CCSR_I3C_EC_STDBYCTRLMODE_STBY_CR_CONTROL, i3c_reg_data); i3c_reg_data = lsu_read_32(SOC_I3CCSR_I3CBASE_HC_CONTROL); i3c_reg_data = (1 << I3CCSR_I3CBASE_HC_CONTROL_BUS_ENABLE_LOW) | i3c_reg_data; lsu_write_32(SOC_I3CCSR_I3CBASE_HC_CONTROL, i3c_reg_data); -
Program I3C core targets with unique static addresses:
- Write to the necessary AXI registers to configure the I3C core with unique static target address.
- Ensure that the AXI interface is properly set up for communication.
- Refer to I3C register documentation at I3C Core Registers for more details.
// Reference Code to Program Unique Static Address for I3C Core uint32_t i3c_reg_data; i3c_reg_data = 0x00000000; //setting device address to 0x5A i3c_reg_data = 0x00000000; i3c_reg_data = 90 << 0 | i3c_reg_data; i3c_reg_data = 1 << 15 | i3c_reg_data; lsu_write_32( SOC_I3CCSR_I3C_EC_STDBYCTRLMODE_STBY_CR_DEVICE_ADDR, i3c_reg_data); //setting virtual device address to 0x5B i3c_reg_data = 0x00000000; i3c_reg_data = 91 << 0 | i3c_reg_data; //0x5B i3c_reg_data = 1 << 15 | i3c_reg_data; lsu_write_32 ( SOC_I3CCSR_I3C_EC_STDBYCTRLMODE_STBY_CR_VIRT_DEVICE_ADDR, i3c_reg_data);
Programming Sequence from GPIO Side
-
Program Dynamic Address for Both Targets:
- Use CCC (Common Command Code) commands to assign dynamic addresses to both I3C targets.
- Ensure that each target is programmed with a unique dynamic address.
-
Read the Dynamic Address for Each Target:
- Verify the dynamic addresses assigned to each target by reading back the addresses.
- Ensure that the addresses are correctly programmed and accessible.
How to test : Smoke & more
-
Basic Connectivity
- CCC command execution for programming Dynamic Address
- Program & Read Recovery interface registers
- Write and read TTI interface from each side.
-
Recovery Sequence
- Test transfers the recovery image
- Boots the device using the recovery image
-
MCTP Test seqeunce
- MCTP Test send random 68 bytes of data and PEC to RX queue
- MCU reads and compares the data with expected data
CDC analysis and constraints
Clock Domain Crossing (CDC) analysis is performed on Caliptra Subsystem. The following are the results and recommended constraints for integrators using standard CDC analysis EDA tools.
In an unconstrained environment, several CDC violations are anticipated. CDC analysis requires the addition of constraints to identify valid synchronization mechanisms and/or static/pseudo-static signals.
Known Issues
Will not affect complete solution.
- Suppressible error in Line 235 of entropy_src.sv
- Occurs in stub code. Comment out to proceed
- Function definition in Line 45 of caliptra_prim_sparse_fsm_flop.sv may cause CDC tool to quit compilation
- CALIPTRA_INC_ASSERT not defined by default
- Comment out code under if condition for CDC analysis
Analysis of missing synchronizers
- All of the signals, whether single-bit or multi-bit, originate from the CaliptraClockDomain clock and their endpoint is the RISCV JTAG clock domain in both Caliptra Core and MCU.
- The violations occur on the read path to the JTAG.
- We only need to synchronize the controlling signal for this interface.
- Inside the dmi_wrapper, the dmi_reg_en and dmi_reg_rd_en comes from dmi_jtag_to_core_sync, which is a 2FF synchronizer.
The following code snippets and schematic diagrams illustrate the CDC violations that end at the JTAG interface.
Figure: Code snippet showing JTAG-originating CDC violations
el2_dbg.sv
rvdffe #(32) dmi_rddata_reg (.din(dmi_reg_rdata_din[31:0]), .dout(dmi_reg_rdata[31:0]), .en(dmi_reg_en), .rst_l(dbg_dm_rst_l), .clk(clk), .*);
soc_ifc_top.sv
cptra_uncore_dmi_reg_rdata <= cptra_uncore_dmi_unlocked_reg_en ? cptra_uncore_dmi_unlocked_reg_rdata_in :
cptra_uncore_dmi_locked_reg_en ? cptra_uncore_dmi_locked_reg_rdata_in : cptra_uncore_dmi_reg_rdata;
dmi_mux.v
// Read mux
assign dmi_rdata = is_uncore_aperture ? dmi_uncore_rdata : dmi_core_rdata;
Figure: Schematic diagram showing JTAG-originating CDC violations




CDC analysis conclusions
- Missing synchronizers appear to be the result of βinferredβ and/or only 2-FF instantiated synchronizers.
- dmi_jtag_to_core_sync.v contains inferred 2FF synchronizers on the control signals βdmi_reg_wr_enβ and βdmi_reg_rd_enβ.
- 2FF synchronizer inferences are considered non-compliant and should be replaced by an explicitly instantiated synchronization module, which is intended to be substituted on a per-integrator basis.
- cdc report scheme two_dff -severity violation
- Multi-bit signals are effectively pseudo-static and are qualified by synchronized control qualifiers.
- Pseudo-static: wr_data, wr_addr
- cdc signal reg_wr_data -module dmi_wrapper -stable
- cdc signal reg_wr_addr -module dmi_wrapper -stable
- Pseudo-static: wr_data, wr_addr
- The core clock frequency must be at least twice the TCK clock frequency of each TAP EPs for the JTAG data to pass correctly through the synchronizers.
CDC constraints
- cdc report scheme two_dff -severity violation
- cdc signal reg_wr_data -module dmi_wrapper -stable
- cdc signal reg_wr_addr -module dmi_wrapper -stable
- cdc signal rd_data -module dmi_wrapper -stable
- cdc signal reg_wr_data -module css_mcu0_dmi_wrapper -stable
- cdc signal reg_wr_addr -module css_mcu0_dmi_wrapper -stable
- cdc signal rd_data -module css_mcu0_dmi_wrapper -stable
- cdc signal jtag_dmi_req_i -module dmi_cdc -stable
- cdc signal jtag_dmi_resp_o -module dmi_cdc -stable
- cdc signal core_dmi_req_o -module dmi_cdc -stable
- cdc signal core_dmi_resp_i -module dmi_cdc -stable
Reset Domain Crossing
Reset Architecture
The below diagram illustrates the internal reset architecture of Caliptra SS.

The reset block diagram below illustrates the RTL implemenation of various resets.

The below diagram illustrates how various internal blocks are connected to either primary resets or internally generated resets.

Similar to Caliptra Core, we solve the problem of RDC using gated clocks which are gated during reset assertion. Below diagram illustrates the clock connections of various sub-modules.

Reset Domain Stamping and Constraints
The Below table illustrates various reset domains that we have in the design. We assume that all resets are asserted at timestamp 5, and the constrained signal is held high or low around that time (for instance, from timestamp 2 to timestamp 8).
| Reset Group | Description | HW Reset Signal | Constraints | False path groups |
|---|---|---|---|---|
| CPTRA_SS_PWRGD | Primary reset input corresponding to SOC Powergood | cptra_ss_pwrgood_i | CPTRA_SS_PWRGD -> all | |
| CPTRA_SS_PRIM_RST | Primary reset input corresponding to SOC Warm Reset | cptra_ss_rst_b_i | caliptra_top_dut.soc_ifc_top1.soc_ifc_reg_hwif_out.CPTRA_FUSE_WR_DONE.done.value -> HIGH i3c.i3c.xrecovery_handler.xrecovery_executor.image_activated_o -> LOW i3c.i3c.xrecovery_handler.xrecovery_executor.payload_available_q -> LOW | CPTRA_SS_PRIM_RST -> CPTRA_CORE_UC_RST CPTRA_SS_PRIM_RST -> CPTRA_CORE_NON_CORE_RST CPTRA_SS_PRIM_RST -> CPTRA_SS_RST CPTRA_SS_PRIM_RST -> CPTRA_SS_MCU_RST |
| CPTRA_SS_RST | Caliptra SS MCI Boot Sequencer generated reset used by various other SS level logic blocks and Caliptra Core | cptra_ss_mci_cptra_rst_b_i mci_top_i.i_boot_seqr.cptra_ss_rst_b_o | mci_top_i.i_boot_seqr.rdc_clk_dis -> HIGH mci_top_i.i_boot_seqr.early_warm_reset_warn -> HIGH mci_top_i.i_boot_seqr.boot_fsm[3:0] = BOOT_IDLE i3c.i3c.xrecovery_handler.xrecovery_executor.image_activated_o -> LOW i3c.i3c.xrecovery_handler.xrecovery_executor.payload_available_q -> LOW caliptra_top_dut.soc_ifc_top1.soc_ifc_reg_hwif_out.CPTRA_FUSE_WR_DONE.done.value -> HIGH | CPTRA_SS_RST -> CPTRA_SS_PRIM_RST CPTRA_SS_RST -> CPTRA_CORE_NON_CORE_RST CPTRA_SS_RST -> CPTRA_CORE_UC_RST CPTRA_SS_RST -> CPTRA_SS_MCU_RST CPTRA_SS_RST -> CPTRA_DMI_NON_CORE_RST |
| CPTRA_CORE_NON_CORE_RST | Caliptra Core Boot FSM generated reset used by various other Caliptra Core logics | caliptra_top_dut.soc_ifc_top1.i_soc_ifc_boot_fsm.cptra_noncore_rst_b | caliptra_top_dut.soc_ifc_top1.i_soc_ifc_boot_fsm.rdc_clk_dis -> HIGH caliptra_top_dut.soc_ifc_top1.i_soc_ifc_boot_fsm.arc_IDLE -> HIGH mci_top_i.i_boot_seqr.rdc_clk_dis -> HIGH | CPTRA_CORE_NON_CORE_RST -> CPTRA_SS_RST CPTRA_CORE_NON_CORE_RST -> CPTRA_SS_PRIM_RST CPTRA_CORE_NON_CORE_RST -> CPTRA_CORE_UC_RST CPTRA_CORE_NON_CORE_RST -> CPTRA_SS_MCU_RST |
| CPTRA_CORE_UC_RST | Caliptra Core Boot FSM generated microcontroller reset for Caliptra Core RISCV | caliptra_top_dut.soc_ifc_top1.i_soc_ifc_boot_fsm.cptra_uc_rst_b | caliptra_top_dut.soc_ifc_top1.i_soc_ifc_boot_fsm.fw_update_rst_window -> HIGH caliptra_top_dut.aes_inst.aes_inst.u_aes_core.u_aes_control.gen_fsm[0].gen_fsm_p.u_aes_control_fsm_i.u_aes_control_fsm.aes_ctrl_cs[5:0] -> 6'b001001 caliptra_top_dut.sha3.hsel_i -> LOW caliptra_top_dut.aes_inst.aes_cif_req_dv -> LOW | |
| CPTRA_SS_MCU_RST | Caliptra SS MCI Boot Sequencer generated microcontroller reset for Caliptra SS RISCV | mci_top_i.i_boot_seqr.mcu_rst_b | mci_top_i.i_boot_seqr.fw_update_rst_window -> HIGH mci_top_i.i_mci_mcu_trace_buffer.mcu_trace_rv_i_valid_ip -> LOW |
The current analysis only focuses on RDC crossing which are internal to Caliptra SS. Any RDC crossings with external flops need to be handled by integrators.
Reset Sequencing
The below waveform illustrates how various resets of Caliptra SS and Caliptra Core are sequenced in the design.

The red and blue line indicates that the input Caliptra SS warm reset (cptra_ss_rst_b_i) needs to be asserted for atleast 32 clock cycles for the reset assertion to propagate through various levels of hierarhcy.
RDC Waivers
All Caliptra Core waivers are applicable at SS level as well. On top of those, we have the following waivers.
create_view_criteria \
-name otp_broadcast_ss_rst_caliptra_core \
-rule {E_RST_METASTABILITY} \
-comment "waiver_for otp_broadcast related paths" \
-criteria { ((ResetFlop =~ "*.u_caliptra_ss.u_otp_ctrl.otp_broadcast_o.secret_prod_partition_0_data.*") || \
(ResetFlop =~ "*.u_caliptra_ss.u_otp_ctrl.otp_broadcast_o.valid*") ) && \
((MetaStableFlop =~ "*.u_caliptra_ss.caliptra_top_dut.soc_ifc_top1.i_soc_ifc_reg.field_storage.fuse_field_entropy*") || \
(MetaStableFlop =~ "*.u_caliptra_ss.caliptra_top_dut.soc_ifc_top1.i_soc_ifc_reg.field_storage.fuse_uds_seed*") ) } \
-replace
set_rule_status -use_view_criteria otp_broadcast_ss_rst_caliptra_core -status Waived -weakmatchfullgroup
create_view_criteria \
-name path_ending_at_2ff_sync \
-rule {E_RST_METASTABILITY} \
-comment "wavier for 2ff sync" \
-criteria { ((MetaStableFlop =~ "*.u_sync_1.gen_generic.u_impl_generic.*") || \
(MetaStableFlop =~ "*.i_rst_window_sync.*") ) } \
-replace
set_rule_status -use_view_criteria path_ending_at_2ff_sync -status Waived -weakmatchfullgroup
create_view_criteria \
-name no_axi_bus_active \
-rule {E_RDC_METASTABILITY} \
-comment "Before warm reset assertion, it is sure that is transaction pending on axi bus" \
-criteria { (ResetFlop =~ "*.caliptra_top_dut.s_axi_active[2:0]") && \
(MetaStableFlop =~ "*cg.user_soc_ifc_icg.clk_slcg_0_generated_icg.p_clkgate.vudpi0.q") } \
-replace
set_rule_status -use_view_criteria no_axi_bus_active -status Waived -weakmatchfullgroup
Synthesis
Synthesis has been performed at CALIPTRA_SS_WRAPPER level which encompasses the following :-
- caliptra_ss_top
- Memories instantiated outside using tech specific macros
Design converges at 400MHz 0.72V using an industry standard, advanced technology node as of 2025 April.
Recommended LINT rules
A standardized set of lint rules is used to sign off on each release. The lint policy may be provided directly to integrators upon request to ensure lint is clean in the SoC.
Known Lint Issue
The following lint violations are known and expected in the current implementation:
Signal Width Mismatches
| Location | Description | Justification |
|---|---|---|
| mcu_mbox_csr.sv:271 | Signal width mismatch | MSB on RHS will be optimized out during synthesis |
Undriven signals
These are undriven signals and deemed to be OK. If exposed to SOC leave unconnected when integrating.
| Location | Signal | Justification |
|---|---|---|
el2_veer.sv | sb_axi_bready_ahb | Caliptra Core internal RV processor uses AHB, not AXI interface, so AXI is unconnected |
el2_veer.sv | ifu_axi_bready_ahb | Caliptra Core internal RV processor uses AHB, not AXI interface, so AXI is unconnected |
el2_veer.sv | lsu_axi_bready_ahb | Caliptra Core internal RV processor uses AHB, not AXI interface, so AXI is unconnected |
Terminology
| Abbreviation | Term | Description |
|---|---|---|
| AXI | Advanced eXtensible Interface | A high-performance, high-frequency communication protocol |
| I3C | Improved Inter-Integrated Circuit | A communication protocol for connecting sensors and other peripherals. |
| ECC | Error Correction Code | A method of detecting and correcting errors in data storage and transmission. |
| RISCV | Reduced Instruction Set Computer Five | An open standard instruction set architecture based on established RISC principles. |
| MCU | Manufacturer Control Unit | A small computer on a single integrated circuit containing a processor core, memory, and programmable input/output peripherals. |
| MCI | Manufacturer Control Interface (for MCU) | Manages the communication between the processor and the memory components. |
| FC | Fuse Controller | A one-time programmable memory controller used for storing critical configuration data and security keys. |
| LCC | Life Cycle Controller | Manages the different stages of the subsystem's lifecycle, including initialization, operation, and shutdown. |
2041523
| Index | Partition | Size [B] | Access Granule | Item | Byte Address | Size [B] |
|---|---|---|---|---|---|---|
| 0 | SW_TEST_UNLOCK_PARTITION | 72 | 32bit | CPTRA_SS_MANUF_DEBUG_UNLOCK_TOKEN | 0x000 | 64 |
| 64bit | SW_TEST_UNLOCK_PARTITION_DIGEST | 0x040 | 8 | |||
| 1 | SECRET_MANUF_PARTITION | 80 | 64bit | CPTRA_CORE_UDS_SEED | 0x048 | 64 |
| 64bit | SECRET_MANUF_PARTITION_DIGEST | 0x088 | 8 | |||
| 64bit | SECRET_MANUF_PARTITION_ZER | 0x090 | 8 | |||
| 2 | SECRET_PROD_PARTITION_0 | 24 | 64bit | CPTRA_CORE_FIELD_ENTROPY_0 | 0x098 | 8 |
| 64bit | SECRET_PROD_PARTITION_0_DIGEST | 0x0A0 | 8 | |||
| 64bit | SECRET_PROD_PARTITION_0_ZER | 0x0A8 | 8 | |||
| 3 | SECRET_PROD_PARTITION_1 | 24 | 64bit | CPTRA_CORE_FIELD_ENTROPY_1 | 0x0B0 | 8 |
| 64bit | SECRET_PROD_PARTITION_1_DIGEST | 0x0B8 | 8 | |||
| 64bit | SECRET_PROD_PARTITION_1_ZER | 0x0C0 | 8 | |||
| 4 | SECRET_PROD_PARTITION_2 | 24 | 64bit | CPTRA_CORE_FIELD_ENTROPY_2 | 0x0C8 | 8 |
| 64bit | SECRET_PROD_PARTITION_2_DIGEST | 0x0D0 | 8 | |||
| 64bit | SECRET_PROD_PARTITION_2_ZER | 0x0D8 | 8 | |||
| 5 | SECRET_PROD_PARTITION_3 | 24 | 64bit | CPTRA_CORE_FIELD_ENTROPY_3 | 0x0E0 | 8 |
| 64bit | SECRET_PROD_PARTITION_3_DIGEST | 0x0E8 | 8 | |||
| 64bit | SECRET_PROD_PARTITION_3_ZER | 0x0F0 | 8 | |||
| 6 | SW_MANUF_PARTITION | 520 | 32bit | CPTRA_CORE_ANTI_ROLLBACK_DISABLE | 0x0F8 | 4 |
| 32bit | CPTRA_CORE_IDEVID_CERT_IDEVID_ATTR | 0x0FC | 96 | |||
| 32bit | SOC_SPECIFIC_IDEVID_CERTIFICATE | 0x15C | 4 | |||
| 32bit | CPTRA_CORE_IDEVID_MANUF_HSM_IDENTIFIER | 0x160 | 16 | |||
| 32bit | CPTRA_CORE_SOC_STEPPING_ID | 0x170 | 4 | |||
| 32bit | CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_0 | 0x174 | 48 | |||
| 32bit | CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_1 | 0x1A4 | 48 | |||
| 32bit | CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_2 | 0x1D4 | 48 | |||
| 32bit | CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_3 | 0x204 | 48 | |||
| 32bit | CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_4 | 0x234 | 48 | |||
| 32bit | CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_5 | 0x264 | 48 | |||
| 32bit | CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_6 | 0x294 | 48 | |||
| 32bit | CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_7 | 0x2C4 | 48 | |||
| 64bit | SW_MANUF_PARTITION_DIGEST | 0x2F8 | 8 | |||
| 7 | SECRET_LC_TRANSITION_PARTITION | 184 | 64bit | CPTRA_SS_TEST_UNLOCK_TOKEN_1 | 0x300 | 16 |
| 64bit | CPTRA_SS_TEST_UNLOCK_TOKEN_2 | 0x310 | 16 | |||
| 64bit | CPTRA_SS_TEST_UNLOCK_TOKEN_3 | 0x320 | 16 | |||
| 64bit | CPTRA_SS_TEST_UNLOCK_TOKEN_4 | 0x330 | 16 | |||
| 64bit | CPTRA_SS_TEST_UNLOCK_TOKEN_5 | 0x340 | 16 | |||
| 64bit | CPTRA_SS_TEST_UNLOCK_TOKEN_6 | 0x350 | 16 | |||
| 64bit | CPTRA_SS_TEST_UNLOCK_TOKEN_7 | 0x360 | 16 | |||
| 64bit | CPTRA_SS_TEST_EXIT_TO_MANUF_TOKEN | 0x370 | 16 | |||
| 64bit | CPTRA_SS_MANUF_TO_PROD_TOKEN | 0x380 | 16 | |||
| 64bit | CPTRA_SS_PROD_TO_PROD_END_TOKEN | 0x390 | 16 | |||
| 64bit | CPTRA_SS_RMA_TOKEN | 0x3A0 | 16 | |||
| 64bit | SECRET_LC_TRANSITION_PARTITION_DIGEST | 0x3B0 | 8 | |||
| 8 | SVN_PARTITION | 40 | 32bit | CPTRA_CORE_FMC_KEY_MANIFEST_SVN | 0x3B8 | 4 |
| 32bit | CPTRA_CORE_RUNTIME_SVN | 0x3BC | 16 | |||
| 32bit | CPTRA_CORE_SOC_MANIFEST_SVN | 0x3CC | 16 | |||
| 32bit | CPTRA_CORE_SOC_MANIFEST_MAX_SVN | 0x3DC | 4 | |||
| 9 | VENDOR_TEST_PARTITION | 64 | 32bit | VENDOR_TEST | 0x3E0 | 32 |
| 64bit | VENDOR_TEST_PARTITION_DIGEST | 0x418 | 8 | |||
| 10 | VENDOR_HASHES_MANUF_PARTITION | 64 | 32bit | CPTRA_CORE_VENDOR_PK_HASH_0 | 0x420 | 48 |
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_0 | 0x450 | 4 | |||
| 64bit | VENDOR_HASHES_MANUF_PARTITION_DIGEST | 0x458 | 8 | |||
| 11 | VENDOR_HASHES_PROD_PARTITION | 864 | 32bit | CPTRA_SS_OWNER_PK_HASH | 0x460 | 48 |
| 32bit | CPTRA_SS_OWNER_PQC_KEY_TYPE | 0x490 | 4 | |||
| 32bit | CPTRA_SS_OWNER_PK_HASH_VALID | 0x494 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_1 | 0x498 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_1 | 0x4C8 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_2 | 0x4CC | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_2 | 0x4FC | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_3 | 0x500 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_3 | 0x530 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_4 | 0x534 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_4 | 0x564 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_5 | 0x568 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_5 | 0x598 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_6 | 0x59C | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_6 | 0x5CC | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_7 | 0x5D0 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_7 | 0x600 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_8 | 0x604 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_8 | 0x634 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_9 | 0x638 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_9 | 0x668 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_10 | 0x66C | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_10 | 0x69C | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_11 | 0x6A0 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_11 | 0x6D0 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_12 | 0x6D4 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_12 | 0x704 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_13 | 0x708 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_13 | 0x738 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_14 | 0x73C | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_14 | 0x76C | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_15 | 0x770 | 48 | |||
| 32bit | CPTRA_CORE_PQC_KEY_TYPE_15 | 0x7A0 | 4 | |||
| 32bit | CPTRA_CORE_VENDOR_PK_HASH_VALID | 0x7A4 | 16 | |||
| 64bit | VENDOR_HASHES_PROD_PARTITION_DIGEST | 0x7B8 | 8 | |||
| 12 | VENDOR_REVOCATIONS_PROD_PARTITION | 216 | 32bit | CPTRA_SS_OWNER_ECC_REVOCATION | 0x7C0 | 4 |
| 32bit | CPTRA_SS_OWNER_LMS_REVOCATION | 0x7C4 | 4 | |||
| 32bit | CPTRA_SS_OWNER_MLDSA_REVOCATION | 0x7C8 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_0 | 0x7CC | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_0 | 0x7D0 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_0 | 0x7D4 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_1 | 0x7D8 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_1 | 0x7DC | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_1 | 0x7E0 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_2 | 0x7E4 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_2 | 0x7E8 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_2 | 0x7EC | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_3 | 0x7F0 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_3 | 0x7F4 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_3 | 0x7F8 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_4 | 0x7FC | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_4 | 0x800 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_4 | 0x804 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_5 | 0x808 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_5 | 0x80C | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_5 | 0x810 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_6 | 0x814 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_6 | 0x818 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_6 | 0x81C | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_7 | 0x820 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_7 | 0x824 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_7 | 0x828 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_8 | 0x82C | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_8 | 0x830 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_8 | 0x834 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_9 | 0x838 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_9 | 0x83C | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_9 | 0x840 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_10 | 0x844 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_10 | 0x848 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_10 | 0x84C | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_11 | 0x850 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_11 | 0x854 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_11 | 0x858 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_12 | 0x85C | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_12 | 0x860 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_12 | 0x864 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_13 | 0x868 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_13 | 0x86C | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_13 | 0x870 | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_14 | 0x874 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_14 | 0x878 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_14 | 0x87C | 4 | |||
| 32bit | CPTRA_CORE_ECC_REVOCATION_15 | 0x880 | 4 | |||
| 32bit | CPTRA_CORE_LMS_REVOCATION_15 | 0x884 | 4 | |||
| 32bit | CPTRA_CORE_MLDSA_REVOCATION_15 | 0x888 | 4 | |||
| 64bit | VENDOR_REVOCATIONS_PROD_PARTITION_DIGEST | 0x890 | 8 | |||
| 13 | VENDOR_SECRET_PROD_PARTITION | 528 | 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_0 | 0x898 | 32 |
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_1 | 0x8B8 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_2 | 0x8D8 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_3 | 0x8F8 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_4 | 0x918 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_5 | 0x938 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_6 | 0x958 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_7 | 0x978 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_8 | 0x998 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_9 | 0x9B8 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_10 | 0x9D8 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_11 | 0x9F8 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_12 | 0xA18 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_13 | 0xA38 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_14 | 0xA58 | 32 | |||
| 64bit | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_15 | 0xA78 | 32 | |||
| 64bit | VENDOR_SECRET_PROD_PARTITION_DIGEST | 0xA98 | 8 | |||
| 64bit | VENDOR_SECRET_PROD_PARTITION_ZER | 0xAA0 | 8 | |||
| 14 | VENDOR_NON_SECRET_PROD_PARTITION | 520 | 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_0 | 0xAA8 | 32 |
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_1 | 0xAC8 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_2 | 0xAE8 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_3 | 0xB08 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_4 | 0xB28 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_5 | 0xB48 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_6 | 0xB68 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_7 | 0xB88 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_8 | 0xBA8 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_9 | 0xBC8 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_10 | 0xBE8 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_11 | 0xC08 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_12 | 0xC28 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_13 | 0xC48 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_14 | 0xC68 | 32 | |||
| 32bit | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_15 | 0xC88 | 32 | |||
| 64bit | VENDOR_NON_SECRET_PROD_PARTITION_DIGEST | 0xCA8 | 8 | |||
| 15 | CPTRA_SS_LOCK_HEK_PROD_0 | 48 | 32bit | CPTRA_SS_LOCK_HEK_PROD_0_RATCHET_SEED | 0xCB0 | 32 |
| 64bit | CPTRA_SS_LOCK_HEK_PROD_0_DIGEST | 0xCD0 | 8 | |||
| 64bit | CPTRA_SS_LOCK_HEK_PROD_0_ZER | 0xCD8 | 8 | |||
| 16 | CPTRA_SS_LOCK_HEK_PROD_1 | 48 | 32bit | CPTRA_SS_LOCK_HEK_PROD_1_RATCHET_SEED | 0xCE0 | 32 |
| 64bit | CPTRA_SS_LOCK_HEK_PROD_1_DIGEST | 0xD00 | 8 | |||
| 64bit | CPTRA_SS_LOCK_HEK_PROD_1_ZER | 0xD08 | 8 | |||
| 17 | CPTRA_SS_LOCK_HEK_PROD_2 | 48 | 32bit | CPTRA_SS_LOCK_HEK_PROD_2_RATCHET_SEED | 0xD10 | 32 |
| 64bit | CPTRA_SS_LOCK_HEK_PROD_2_DIGEST | 0xD30 | 8 | |||
| 64bit | CPTRA_SS_LOCK_HEK_PROD_2_ZER | 0xD38 | 8 | |||
| 18 | CPTRA_SS_LOCK_HEK_PROD_3 | 48 | 32bit | CPTRA_SS_LOCK_HEK_PROD_3_RATCHET_SEED | 0xD40 | 32 |
| 64bit | CPTRA_SS_LOCK_HEK_PROD_3_DIGEST | 0xD60 | 8 | |||
| 64bit | CPTRA_SS_LOCK_HEK_PROD_3_ZER | 0xD68 | 8 | |||
| 19 | CPTRA_SS_LOCK_HEK_PROD_4 | 48 | 32bit | CPTRA_SS_LOCK_HEK_PROD_4_RATCHET_SEED | 0xD70 | 32 |
| 64bit | CPTRA_SS_LOCK_HEK_PROD_4_DIGEST | 0xD90 | 8 | |||
| 64bit | CPTRA_SS_LOCK_HEK_PROD_4_ZER | 0xD98 | 8 | |||
| 20 | CPTRA_SS_LOCK_HEK_PROD_5 | 48 | 32bit | CPTRA_SS_LOCK_HEK_PROD_5_RATCHET_SEED | 0xDA0 | 32 |
| 64bit | CPTRA_SS_LOCK_HEK_PROD_5_DIGEST | 0xDC0 | 8 | |||
| 64bit | CPTRA_SS_LOCK_HEK_PROD_5_ZER | 0xDC8 | 8 | |||
| 21 | CPTRA_SS_LOCK_HEK_PROD_6 | 48 | 32bit | CPTRA_SS_LOCK_HEK_PROD_6_RATCHET_SEED | 0xDD0 | 32 |
| 64bit | CPTRA_SS_LOCK_HEK_PROD_6_DIGEST | 0xDF0 | 8 | |||
| 64bit | CPTRA_SS_LOCK_HEK_PROD_6_ZER | 0xDF8 | 8 | |||
| 22 | CPTRA_SS_LOCK_HEK_PROD_7 | 48 | 32bit | CPTRA_SS_LOCK_HEK_PROD_7_RATCHET_SEED | 0xE00 | 32 |
| 64bit | CPTRA_SS_LOCK_HEK_PROD_7_DIGEST | 0xE20 | 8 | |||
| 64bit | CPTRA_SS_LOCK_HEK_PROD_7_ZER | 0xE28 | 8 | |||
| 23 | LIFE_CYCLE | 88 | 32bit | LC_TRANSITION_CNT | 0xE30 | 48 |
| 32bit | LC_STATE | 0xE60 | 40 |
Subsystem Registers
Redirecting to external documentation...
Click here if not redirected β
be6a343
Introduction
This document summarizes the current state of the I3C core developed by Antmicro for Caliptra within CHIPS Alliance.
The implementation follows the Errata 01 for MIPI I3C Basic Specification Version 1.1.1 dated 11.03.2022.
Documentation structure
This documentation comprises the following chapters:
- overview - summarizes the main notions of the project
- ccc - provides an overview of the CCCs implemented by the core
- phy - provides a description of the I3C PHY Layer logic
- dv - describes verification tooling and testplans
- ext_cap - provides a description of Target Transaction Interface
- i3c_recovery_flow - describes the I3C-based Recovery mode workflow
- axi_id_filtering - provides information about the AXI transactions filtering feature
- axi_recovery_flow - describes the alternative, optional, recovery flow where the recovery data is transferred to the core over the AXI bus
- registers - provides auto-generated register descriptions
- known_limitations - provides information about known core limitaions in specific releases
be6a343
I3C core overview
This chapter provides a high level overview of the I3C target functionalities implemented in the core.
Configuration script
The I3C Core is configured with the i3c_core_config Python script, which reads configurations from the i3c_core_configs.yaml YAML file.
The supported configurations can be found in the i3c_core_configs.yaml file.
More details on the usage of the tool can be found in the relevant README.
System Bus
The I3C core can connect to system buses via dedicated adapters:
- AXI4
- AHB - The AHB-Lite implementation is based on the AMBA 3 AHB-Lite Protocol Specification (IHI0033A)
Virtual target
The I3C core implements the Secure Firmware Recovery protocol according to the Open Compute "Secure Firmware Recovery" specification rev. 1.1-rc5.
For this purpose, the I3C core exposes a "virtual" target with its own static and dynamic bus addresses.
The virtual target implementation shares most of its logic with the "main" one while retaining a distinct data path.
Certain CCC commands like SETAASA and SETDASA are implemented separately for the "main" and "virtual" targets.
Register descriptions
Register descriptions are specified in the RDL format for I3C core CSRs:
- I3C Capability and Operational Registers (I3CBase)
- Programmable I/O (PIOControl)
- Extended Capabilities (I3C_EC)
- Device Address Table (DAT)
- Device Characteristic Table (DCT)
The RDL files generate the relevant SystemVerilog which can be found in the src/csr/ directory. The auto-generated descriptions are included in the Register descriptions chapter.
Target Interface Queues
There are also target interface queues via Target Transaction Interface (TTI):
- RX - read descriptor & data queues
- TX - write descriptor & data queues
- IBI - IBI combined descriptor + data queue
Target recovery interface
Several functionalities related to the recovery interface have been implemented for Caliptra:
-
Recovery mode enable control via a CSR field
-
Hardware recovery packet handling (private read/write)
-
Hardware PEC checksum calculation and checking
-
Access to Recovery CSRs from the I3C side
-
Status signaling via output pins:
recovery_payload_available_orecovery_image_activated_o
Private reads and writes
-
The core handles I3C private reads and writes
-
This functionality passes Avery test suite tests
-
Private writes push data to the TTI RX Queue, accessible from the AXI bus, allowing the CPU to read the data
- The number of received bytes is written into the TTI RX descriptor Queue
- The software is supposed to first read the descriptor data, and then the number of bytes defined by the descriptor for the TTI RX Queue
-
Private reads send data on I3C lines from TTI TX Queue
- The software has to write the TTI TX Queue prior to a I3C private read transaction
- The TTI TX descriptor is used similarly to set the max number of bytes to be sent in the next private read transaction
-
In-Band Interrupts (IBI)
The core is capable of raising In-Band Interrupts. IBIs are controlled using descriptors written to a dedicated IBI queue by software. Optional IBI data immediately follows the descriptor in the same queue.
The core watches the IBI queue for a descriptor write. Once a descriptor is written, the core peeks it and waits until the defined count of data words is written to the queue. Finally, the core outputs the Mandatory Data Byte (MDB) and the data as 8-bit words.
I3C Common Command Codes (CCC)
The I3C core supports all CCCs required by the I3C Basic spec, please see "Table 16 I3C Common Command Codes" for a full reference.
All CCCs are exercised with Cocotb tests.
Broadcast CCCs
The following Broadcast CCCs are currently supported by the core (all required Broadcast CCCs as per the errata, and one optional Broadcast CCC):
- ENEC (R) - Enable Events Command
- DISEC (R) - Disable Events Command
- SETMWL (R) - Set Max Write Length
- SETMRL (R) - Set Max Read Length
- SETAASA (O) - Set All Addresses to Static Adresses
- RSTACT (R) - Target Reset Action
Direct CCCs
The following Direct CCCs are currently supported by the core (all required Direct CCCs, plus several optional/conditional ones):
- ENEC (R) - Enable Events Command
- DISEC (R) - Disable Events Command
- RSTDAA (R) - Direct Reset Dynamic Address Assignment - this direct CCC is deprecated, the core NACKs this command as per the spec
- SETDASA (O) - Set Dynamic Address from Static Address
- SETMWL (R) - Set Max Write Length
- SETMRL (R) - Set Max Read Length
- GETMWL (R) - Get Max Write Length
- GETMRL (R) - Set Max Read Length
- GETPID (C) - Get Provisioned ID
- GETBCR (C) - Get Bus Characteristics Register
- GETDCR (C) - Get Device Characteristics Register
- GETSTATUS (R) - Get Device Status
- RSTACT (R) - Target Reset Action
Other features
-
Target Reset Pattern is detected and causes assertion of output pins, based on the action selected with RSTACT:
peripheral_reset_oescalated_reset_o
-
The core correctly detects HDR-Exit Pattern
be6a343
Physical Layer
This chapter provides a description of the I3C PHY Layer logic.
Common PHY Layer
The PHY is responsible for controlling external bus signals (SDA, SCL) and synchronizing them with an internal clock. It should also support bus arbitration. The I2C Core from the OpenTitan project can be used as a reference design for basic features of the PHY.
:::{figure-md} i3c_phy

Block diagram of the I3C PHY Layer :::
SDA and SCL lines (5.1.3.1)
Both I2C and I3C datasheets specify that SDA and SCL lines should be bidirectional, connected to an active Open Drain class Pull-Up. Bus lines should be HIGH unless any device ties them to the GROUND.
:::{note}
In addition to the active Open Drain class Pull-Up, a High-Keeper is also required on the Bus.
The High-Keeper on the Bus should be strong enough to prevent system leakage from pulling SDA, and sometimes SCL, Low.
The High-Keeper on the Bus should also be weak enough for a Target with the minimum I{sub}OL PHY to be able to pull SDA, SCL, or both Low within the Minimum t{sub}DIG_L period.
The High-Keeper should be implemented during the physical design. PHY driver strength modeling will not be performed in this project. Base Controller will be delivered without the High-Keeper, however, it may become a configuration option later on. :::
Each bus line must be capable of switching between 4 logic states:
- No Pull-Up (High-Z)
- High-Keeper Pull-Up
- Open Drain Pull-Up
- Assert LOW
The OpenTitan I2C Core implements a Virtual Open Drain functionality which seems like a good solution for implementing the desired behavior on FPGA devices, while at the same time keeping it easy to use in silicon chips. Each bus line consists of 3 lines:
- Signal input (
scl_i,sda_i) - external input from the bus lines. - Signal output (
scl_o,sda_o) - internal signal, it is tied to the GROUND. - Signal output enable (
scl_en_o,sda_en_o) - internal signal enable, controlled by the core FSM.
This interface makes it easy to construct tri-state buffers.
The controller will never assert the external bus lines HIGH, since it is assumed that these lines are pulled up to V{sub}dd externally.
Switching from output to input is enough to achieve signals asserted HIGH.
Verilator does not natively support x and z states and their handling is explained in the official documentation.
Cocotb requires a wrapper to interact properly with an inout, which is described in Cocotb discussion #3506.
Considering these limitations, PHY is being tested functionally using Cocotb and tri-state logic.
Additionally, there is an RTL testbench run in Verilator and Icarus simulators that checks whether High-Z is set properly on I3C bus lines when the controller requests a high bus state.
Clock synchronization (5.1.7)
The entire core functions in a single clock domain - all I3C bus signals are sampled with this clock.
I3C timing configuration
The core implements 4 CSRs for controlling timings of the I3C bus:
T_F_REG- SCL falling timeT_R_REG- SCL rise timeT_HD_DAT_REG- SDA hold timeT_SU_DAT_REG- SDA setup time
In the target configuration, the first three should be set to 0, the T_SU_DAT_REG should be set according to the following equation:
reg_val = $floor(3 / system_clock_period) - 1
T_SU_DAT_REG = reg_val > 0 ? reg_val : 0
The core supports system clock frequencies above 333MHz, and SCL frequencies up to 12.5MHz.
Below 333MHz Tsco I3C timing requirement of 12ns is not met. I3C specification defines Tsco as "Clock to Data Turnaround Time: The time duration between reception of an SCL edge and the start of driving an SDA change".
With 333MHz clock the maximum response time from the core is 12ns. This timing is not affected by the chip pads delays as per MIPI CSI I3C 1.1.1 specification.
The I3C core needs 3 system clock cycles between an event on a SCL line and driving the SDA. Since SCL and core clock are asynchronous, SCL can drop just after rising edge of the system clock. Such situation adds an additional clock cycle latency resulting in 4 cycles in total.
Maximal I3C core Tsco can be calculated with the Tsco = 4 * Tsys_clk formula , where Tsys_clk is a system clock period.
Future core releases will enable the GETMXDS CCC support allowing the core to advertise longer Tsco times for lower system clock frequencies.
be6a343
Design verification
This chapter presents the available models and tools which are used for I3C verification. The core is verified with the Cocotb + unit tests and the UVM test suite.
This section contains testplans for the verification.
Definitions:
testplan- an organized collection of testpointstestpoint- an actionable item, which can be turned into a test:name- typically related to the tested featuredesc- detailed description; should contain description of the feature, configuration mode, stimuli, expected behavior.stage- can be used to assign testpoints to milestones.tests- names of implemented tests, which cover the testpoint. Relation test-testpoint can be many to many.tags- additional tags that can be used to group testpoints
Full overview of tests can be found in Testplan summary{.external}.
Testplans for individual blocks
:heading-offset: 2
Testplans for the core
:heading-offset: 2
Compliance test suite
The list of non-public tests which utilize Avery I3C VIP framework and have been successfully ran against the design is available in the CTS list.
be6a343
Specification for I3C Vendor-Specific Extended Capabilities
This chapter provides a description of Target Transaction Interface that created for the Standby Controller according to the normative specification of the Vendor-Specific Extended Capabilities for the I3C Controller as per section 7.7.13 of the {term}I3C HCI spec.
Security
The HCI specification defines Extended Capabilities as a list of linked lists, which can be discovered from software through a series of CSR reads. This mechanism is unacceptable for the Recovery Mode as memory offsets must be known at synthesis time. Implementation based on this specification should provide a list of known memory offsets for each of the Extended Capabilities.
:::{note} In order to increase security of the solution, the offsets are provided, so software may choose to skip the discovery mechanism. This specification is compliant with the original specification, so the mechanism of discovery can still be used, if needed. :::
Extended Capabilities
Standby Controller Mode - 0x12
The Standby Controller Mode follows the {term}I3C HCI spec:
- Chapter 6 Theory of Operation
- Section 6.17 Standby Controller Mode
- Chapter 7 Register Interface
- Section 7.7.11 Standby Controller Mode
Vendor-specific Extended Capabilities
This specification provides definitions and descriptions of the following Capabilities:
- Controller Config
- Standby Controller Mode
- Secure Firmware Recovery Interface
- Target Transaction Interface
- SoC Management Interface
:::{list-table} Extended capabilities registers :name: tab-capabilities-registers :widths: 60 20 20
-
- Extended Capability
- ID
- Memory Offset
-
- Controller Config
- 0x02
- Implementer
-
- Standby Controller Mode
- 0x12
- Implementer
-
- Secure Firmware Recovery Interface
- 0xC0
- Implementer
-
- SoC Management Interface
- 0xC1
- Implementer
-
- Target Transaction Interface
- 0xC4
- Implementer :::
Controller Config - 0x02
The Controller Config Capability follows section 7.7.3 of the {term}I3C HCI spec.
Secure Firmware Recovery Interface - 0xC0
This section is based on the Open Compute Project Secure Firmware Recovery, Version 1.1-rc5 and the I3C Target Recovery Specification.
The EXTCAP_HEADER is located at the SEC_FW_RECOVERY_OFFSET memory offset.
The registers are aligned to DWORD size (4 bytes), unless specified otherwise.
:::{list-table} Secure Firmware Recovery Interface :name: tab-secure-firmware-recovery-interface :widths: 40 60
-
- Register Name
- Role
-
- EXTCAP_HEADER
- Information about the Extended Capability
-
- PROT_CAP_0
- Device Capabilities Information
-
- PROT_CAP_1
- Device Capabilities Information
-
- PROT_CAP_2
- Device Capabilities Information
-
- PROT_CAP_3
- Device Capabilities Information
-
- DEVICE_ID_0
- Device identity information
-
- DEVICE_ID_1
- Device identity information
-
- DEVICE_ID_2
- Device identity information
-
- DEVICE_ID_3
- Device identity information
-
- DEVICE_ID_4
- Device identity information
-
- DEVICE_ID_5
- Device identity information
-
- DEVICE_ID_6
- Device identity information
-
- DEVICE_STATUS_0
- Device status information
-
- DEVICE_STATUS_1
- Device status information
-
- DEVICE_RESET
- Device reset and control
-
- RECOVERY_CTRL
- Recovery control and image activation
-
- RECOVERY_STATUS
- Recovery status information
-
- HW_STATUS
- Hardware status including temperature
-
- INDIRECT_FIFO_CTRL_0
- Indirect FIFO memory window control
-
- INDIRECT_FIFO_CTRL_1
- Indirect FIFO memory window control
-
- INDIRECT_FIFO_STATUS_0
- Indirect FIFO memory window status
-
- INDIRECT_FIFO_STATUS_1
- Indirect FIFO memory window status
-
- INDIRECT_FIFO_STATUS_2
- Indirect FIFO memory window status
-
- INDIRECT_FIFO_STATUS_3
- Indirect FIFO memory window status
-
- INDIRECT_FIFO_STATUS_4
- Indirect FIFO memory window status
-
- INDIRECT_FIFO_STATUS_5
- Indirect FIFO memory window status
-
- INDIRECT_FIFO_DATA
- Indirect FIFO memory window for pushing recovery image :::
SoC Management Interface - 0xC1
The SoC Management Interface is provided to enable additional configuration capabilities to the system integrator, e.g. programability of PHY Devices.
The SOC_MGMT_EXTCAP_HEADER is located at the SOC_MGMT_SECTION_OFFSET memory offset.
The registers are aligned to DWORD size (4 bytes), unless specified otherwise.
:::{list-table} SoC Management Interface :name: tab-soc-management-interface :widths: 50 50
-
- Register Name
- Role
-
- SOC_MGMT_EXTCAP_HEADER
- Information about the Extended Capability
-
- SOC_MGMT_CONTROL
- TBD
-
- SOC_MGMT_STATUS
- TBD
-
- SOC_MGMT_RSVD_0
- TBD
-
- SOC_MGMT_RSVD_1
- TBD
-
- SOC_MGMT_RSVD_2
- TBD
-
- SOC_MGMT_RSVD_3
- TBD
-
- SOC_MGMT_FEATURE_0
- TBD
-
- SOC_MGMT_FEATURE_1
- TBD
-
- SOC_MGMT_FEATURE_2
- TBD
-
- SOC_MGMT_FEATURE_3
- TBD
-
- β¦
- TBD
-
- SOC_MGMT_FEATURE_15
- TBD :::
Details of the operation are implementation specific. It is permissible to implement all registers as generic RW registers.
Target Transaction Interface - 0xC4
The Target Transaction Interface (TTI) provides additional registers and queues to enable data flow for Devices configured in the Target Mode.
This specification is meant for Standby Controllers which are capable of operating in Target Mode, therefore implementations are required to advertise the TTI by setting the Target_XACT_SUPPORT field of STBY_CR_CAPABILITIES.
:::{note} The term "TX" is used to denote instances in which the software performs a write to respond to an I3C Bus Read transaction. :::
:::{note} The term "RX" is used to denote instances in which the software performs a read to respond to an I3C Bus Write transaction :::
Operation
TTI is used to handle generic Read-Type and Write-Type transactions sent by the Standby Controller on the I3C bus. In this mode, software is responsible for servicing TX and RX queues based on the interrupt signals.
There are 5 queues to communicate between the bus and the register interface:
- TTI TX Descriptor queue, which holds information about read transfer
- TTI TX Data queue to buffer data written from the Target Device to the bus
- TTI RX Descriptor queue, which holds information about write transfer
- TTI RX Data queue to buffer data read from the bus by the Target Device
- TTI IBI queue to buffer data which will be written to the Bus as an In-Band Interrupt
Bus Read Transaction
After the Bus Read Transaction is acknowledged by the Target Device, it performs a write to the Interrupt Register to inform software of a pending read transaction. Software writes the response data to the TX queue and the TX descriptor to the TTI TX Descriptor queue.
Bus Write Transaction
The Bus Write Transaction is acknowledged by the Device if the transaction address matches the Device address and the RnW bit is set to 0.
The Target Device writes incoming bytes to the TTI RX Data Queue.
After the transaction ends, a TTI RX Descriptor is generated and pushed to the TTI RX Descriptor Queue for the software access.
Then, interrupt RX_DESC_STAT is raised.
:::{figure-md} fig-ext-cap-pwrite-timing

Private Write timing diagram :::
During the Private Write transaction, an error can be caused by:
- bit flip, which will be detected by the parity bit
- lack of space in the RX queue (overrun)
:::{figure-md} fig-ext-cap-pwrite-overrun

Private Write timing diagram: Parity bit error or RX Queue overrun scenario :::
If an Active Controller writes more data to the Target Device than it is capable to handle (even with triggering interrupts on threshold), the generated TTI RX Descriptor will indicate an error status and the Target Device should not ACK data on the bus.
The Active Controller can attempt mitigating such a situation by reading Target queue size from the TTI_QUEUE_SIZE register before sending a big chunk of data.
In-Band Interrupts
The Controller expects to receive an IBI Status Descriptor which is then followed by consecutive DWORDs of IBI Data.
:::{figure-md} fig-ext-cap-ibi

IBI Queue: partially filled with 3 interrupts. :::
In order to request an IBI, first the software should read the IBI queue size and set the queue threshold accordingly. Next, the interrupts should be enabled and an IBI descriptor with data can be written to the IBI_DATA_PORT. If, at this time, the IBI_THLD_STAT bit is set, then software should not attempt to write another IBI Descriptor into the queue. Software should wait until the IBI_THLD_STAT is cleared by hardware. The Target device will not try to send the IBI onto the I3C Bus until the bus is in the Available state. Also, it will read the IBI descriptor and wait until the IBI queue has all required data entries*. After meeting these conditions, the IBI will be driven onto the bus. The Target device will raise the IBI_DONE interrupt to notify that the IBI is finished. The software should read the LAST_IBI_STATUS to determine if the IBI ended successfully or was rejected. In case of failure, if the data integrity was not violated, the IBI will be retried once automatically.
:::{figure-md} fig-ext-cap-ibi-timing

IBI Timing Diagram: send an IBI. :::
Register Interface
For a detailed list of TTI registers, refer to the TTI Registers chapter.
Data Structures (descriptors)
The TX Descriptor is 32-bit wide and has the following format:
:::{list-table} Target Transaction Interface TX Descriptor Format :name: tab-tti-target-transaction-interface-tx-descriptor :widths: 30 30 40
-
- Field Name
- Position (Size [bits])
- Description
-
- DATA_LENGTH
- [15:0] (16)
- Number of data bytes in the TX Transaction. :::
The RX Descriptor is 32-bit wide and has the following format:
:::{list-table} Target Transaction Interface RX Descriptor Format :name: tab-tti-target-transaction-interface-rx-descriptor :widths: 30 30 40
-
- Field Name
- Position (Size [bits])
- Description
-
-
ERROR
-
[31:28] (4)
-
Error occurred during this transaction. Software should read and discard data from the RX Queue.
Values:
0x0: Success
0x1: Generic error
0x2-0xF: Reserved, will be used to determine type of error.
-
-
- DATA_LENGTH
- [15:0] (16)
- Number of data bytes in the RX Transaction. :::
The IBI Descriptor is 32-bit wide and has the following format:
:::{list-table} Target Transaction Interface IBI Descriptor Format :name: tab-tti-target-transaction-interface-ibi-descriptor :widths: 30 30 40
-
- Field Name
- Position (Size [bits])
- Description
-
- MDB
- [31:24] (8)
- Mandatory Data Byte. This field is valid if BCR[2] is set.
-
- DATA_LENGTH
- [7:0] (8)
- Number of data bytes in the IBI. :::
TTI Queues
TTI TX Descriptor, TTI TX Data, TTI RX Descriptor, TTI RX Data and TTI IBI queues are implemented as FIFO queues of 32 bit (1 DWORD) width.
Depth should be parametrizable and the TTI_QUEUE_SIZE registerβs reset value should be set accordingly.
In order to prevent overflow/underrun scenarios, a programmable threshold signal is provided.
Software-issued reset of the queues contents is also possible.
be6a343
Recovery flow
The recovery flow is implemented according to the Open Compute Secure Firmware Recovery standard v1.1-rc5. The recovery mode is handled through the virtual I3C target implemented by the core. In the Recovery Mode the Recovery Initiator Device (e.g. BMC) is primarily responsible for streaming the Firmware Recovery Image to the I3C Core. In order to facilitate this process, the I3C Core implements CSRs as specified in the Secure Firmware Recovery Interface. The firmware is responsible for implementing the recovery flow and transferring firmware data to the program memory.
The recovery flow adheres to the following steps:
- Upon reset, the hardware sets the FIFO size and region type in the
INDIRECT_FIFO_STATUSCSR - The device's firmware configures the I3C core and sets the appropriate bits in the
PROT_CAPCSR to indicate its recovery capabilities These must include the mandatory ones:- bit 0 (
DEVICE_ID) - bit 4 (
DEVICE_STATUS) - bit 6 (
Local C-image support) or bit 7 (Push C-image support) - bit 5 (
INDIRECT_CTRL), only if bit 7 is set
- bit 0 (
- Upon request for recovery mode entry, the firmware writes
0x3(Recovery mode) toDEVICE_STATUS - The Recovery Initiator writes to
INDIRECT_FIFO_CTRLto inform the Recovery handler about the image size- Component Memory Space (
CMS) field is set to0
- Component Memory Space (
- The Recovery Initiator writes a data chunk to the receive FIFO via the
INDIRECT_FIFO_DATACSR. The I3C core responds with a NACK when the FIFO is full. - The Recovery Handler updates FIFO pointers (Read Index and Write Index) presented in the
INDIRECT_FIFO_STATUSCSR - The device's firmware reacts to a signal that a data chunk has been written to the FIFO by polling the
INDIRECT_FIFO_STATUSCSR - The device's firmware reads the data chunk from the FIFO and stores it in an appropriate location in the memory
- Steps 5 to 8 are repeated until the Recovery handler detects that the whole firmware image has been transmitted
- The device's firmware polls the
RECOVERY_CONTROLregister until it receives the "Activate image" command from the Recovery Initiator - The device's firmware updates the
RECOVERY_STATUSCSR to indicate that the uploaded firmware is being booted
Recovery handler
Since the OCP Secure Firmware Recovery standard describes a set of CSRs that are the interface between the device being updated and the Recovery Initiator, I3C transactions that access them must be handled in the logic instead of the firmware, which is the purpose of the Recovery Handler block.
When the recovery mode is active, the handler takes over the TTI interface of the controller.
:::{figure-md} recovery_handler

Recovery handler :::
The architecture of the Recovery Handler module is shown in the block diagram below:
:::{figure-md} recovery_handler_bd

Recovery handler architecture :::
The module's backend is connected directly to the controller's TTI interface.
There are several muxes on the RX and TX data paths which allow bypassing the module in normal operation mode and remove/inject data in recovery mode.
There are two PEC (Packet Error Code) blocks responsible for calculating CRC for RX and TX data paths.
The CRC algorithm operates on individual bytes and implements C(x) = x^8+x^2+x^1+1 polynomial (see MCTP I3C binding, section 5.3.1).
The frontend is connected to I3C core CSRs accessible by software.
Normal operation
When the recovery mode is disabled the core allows accessing only a subset of recovery mode CSRs over I3C on the virtual target address. These are the registers marked as "available anytime" by the recovery interface specification. An attempt to access other CSRs results in a NACK response.
Recovery operation
When the recovery mode is enabled all recovery CSRs can be accessible via I3C bus. Accesses to the registers must be performed via the virtual target address.
CSR access via I3C
Recovery mode CSRs are accessible through the I3C bus. The following protocol is used to implement read/write operations:
CSR write
:::{figure-md} csr_write

CSR write :::
The Recovery Initiator sends a command byte, followed by 16-bit payload length LSB and MSB bytes using I3C private transfers. The payload data immediately follows the length bytes. Each transfer ends with an additional byte containing a Packet Error Code (PEC) checksum.
CSR read
:::{figure-md} csr_read

CSR read :::
For CSR read, the Recovery Initiator sends only the command byte and PEC checksum. Next, it issues a repeated start and begins a private read transaction. The device responds by sending data length LSB and MSB bytes, followed by the data and PEC.
Recovery handler operation
The main job of the Recovery Handler is providing hardware means for an active controller to access CSRs relevant to the recovery operation.
CSR write
When the Recovery Initiator tries to write to a CSR through I3C, it first sends the command byte followed by two length bytes which are received by the handler logic.
If the length is non-zero, the handler resets the PEC block and sets R2MUX to pass the remaining data to the TTI RX data queue.
R2MUX disconnects the queue just before the last byte, which is the PEC checksum.
Finally, the last byte is compared with the checksum computed by the PEC block.
If the checksum matches the command, the handling part of the handler logic reads data from the TTI RX data queue and updates relevant CSR fields.
If the checksum does not match, then the handler discards all the data in the queue.
In case of an INDIRECT_FIFO_DATA write command the handler does not process the data at all. Instead, it passes the data to the RX indirect FIFO queue to make it available to the software.
When the software reads data from the INDIRECT_FIFO_DATA CSR, the handler updates the queue pointer in the INDIRECT_FIFO_STATUS CSR.
CSR read
A CSR read begins similarly as write, by receiving the command byte. Following that, the command handling part reads data from the CSR being read and passes it to the transmit part, which formats the response I3C packet.
The transmit logic injects the CSR length, resets the TX PEC module, sends the CSR content to the I3C core and finally injects the calculated PEC checksum.
be6a343
AXI Transaction ID Filtering
In order to facilitate constraining the recovery core's accessibility, the I3C core implements an AXI transaction ID filtering mechanism.
Transaction IDs are passed using the aruser and awuser signals.
The ID filtering logic is optional and depends on the AXI_ID_FILTERING macro definition.
Recovery core configuration includes a parameter denoting the NUM_PRIV_IDS - number of privileged IDs that are granted read and write access when the filtering mechanism is enabled.
Having AXI_ID_FILTERING defined requires NUM_PRIV_IDS to be greater than 0. This is verified with an assertion.
Undefined AXI_ID_FILTERING and non-zero NUM_PRIV_IDS are considered a legal configuration and causes the ID filtering logic to not be included in the design.
The filtering mechanism is controlled via:
disable_axi_filtering_irecovery core port to disable the filtering mechanism1'b0- enable AXI filtering1'b1- disable AXI filtering
[0:AXI_ID_WIDTH-1] priv_ids_i [0:NUM_PRIV_IDS-1]recovery core port containing privileged IDs- Each privileged ID should be of width
AXI_ID_WIDTHpassed atpriv_ids_i[k]for each k in {0 β¦ NUM_PRIV_IDS - 1} - All
NUM_PRIV_IDSentries are expected to be set to valid privileged IDs
- Each privileged ID should be of width
When the filtering is enabled, any transaction attempt outside of the privileged IDs will be met with a SLVERR (0b10) error on the respective AXI response channel.
The above-mentioned disable_axi_filtering_i and priv_ids_i ports will not be included in the design if AXI_ID_FILTERING is not defined.
be6a343
AXI driven Caliptra recovery flow
This chapter discusses the implementation of AXI based recovery flow, which is an alternative to the standard I3C based flow (see recovery_flow). This feature allows driving the recovery data from within the SoC integrating the I3C core over the AXI bus, bypassing I3C communication.
AXI Recovery flow implementation
The AXI recovery flow reuses the logic already present in the I3C core used in the Caliptra-SS design, with a runtime option essentially bypassing most of the I3C core communication logic (including the I3C recovery flow logic).
The loopback functionality is configurable via a CSR, with the I3C mode set as the default.
Recovery CSRs are accessible from the internal AXI bus. The transactions to the core may be filtered using the AXI ID field (see axi_id_filtering)
The logic is implemented so that the recovery firmware in the Caliptra RoT ROM can operate without any changes.
In order to enable setting W1C fields in recovery registers over AXI, the AXI recovery mode introduces additional register - REC_INTF_REG_W1C_ACCESS.
AXI-based recovery procedure
The Caliptra MCU RISC-V core is responsible for driving the data copied from an external memory (e.g. QSPI interface) to the recovery FIFOs. The ROM running on the MCU core monitors the recovery block registers and performs the recovery flow. During the boot procedure the ROM will have to follow the following procedure:
- Set the I3C block to the "direct AXI" mode
- Set the
REC_INTF_BYPASSbit in theREC_INTF_CFGregister
- Set the
- Poll the
DEVICE_STATUS_0register and wait for the recovery to be enabled by the Caliptra core- Poll
DEVICE_STATUS_0.DEV_STATUSuntil it becomes0x03(Recovery mode - ready to accept recovery image)
- Poll
- Read the
RECOVERY_STATUSregister and check if the recovery flow started- Check whether
RECOVERY_STATUS.DEV_REC_STATUSis0x1(Awaiting recovery image)
- Check whether
- Write to the
RECOVERY_CTRLregister to set the recovery image configuration - Write to the
INDIRECT_FIFO_CTRL_0register to reset the FIFO - Write to the
INDIRECT_FIFO_CTRL_1register to set the recovery image size - Push the recovery image to the recovery interface FIFOs:
- Read the
INDIRECT_FIFO_STATUSregister to determine remaining space in the indirect FIFO- If the indirect FIFO is not full, write a chunk of data to the
TX_DATA_PORTregister - If the indirect FIFO is full, wait for the
INDIRECT_FIFO_STATUS_0.FULLbit to clear
- If the indirect FIFO is not full, write a chunk of data to the
- The above steps should be repeated until the whole recovery image is written to the FIFO
- Read the
- Notify the recovery handler that the final chunk has been sent
- Set the
REC_PAYLOAD_DONEbit in theREC_INTF_CFGregister to raise thepayload_availablesignal for the final time
- Set the
- Activate the new image
- Write
0xf00to theREC_INTF_REG_W1C_ACCESSregister to update theRECOVERY_CTRLfield
- Write
- Read the
RECOVERY_STATUSregister to ensure the image has been activated- Poll
RECOVERY_STATUS.DEV_REC_STATUSuntil it becomes0x3(Recovery successful); before the image is activated, this field will be0x2(Booting recovery image)
- Poll
The recovery image will be written in chunks with length equal to or less than Max transfer size defined in the INDIRECT_FIFO_STATUS register.
Once the last data chunk is written to the FIFO, the Caliptra MCU ROM will write a CSR in the Secure Firmware Recovery register file indicating the transfer is complete.
Recovery Handler bypass
In the regular (I3C) mode of the core, the Recovery Handler strongly relies on communication with the I3C Core internal logic by interfacing with TTI Queues. The bypass implementation modifies the I3C Core logic to allow direct access over the AXI bus to the structures specified by the OCP Secure Firmware Recovery for compliance with the Caliptra Subsystem Recovery Sequence.
The default design of the Recovery Handler includes many blocks specifically designed to translate I3C bus traffic into recovery messages. It also automatically responds to the I3C commands by writing transaction descriptors and data for the TTI Queues. Such a recovery flow is presented in the diagram below.
:::{figure-md} recovery_handler

Recovery Handler in the I3C Core :::
In order enable an alternative recovery mechanism while reusing the existing logic and keeping compliance with Caliptra, the I3C core provides a custom bypass feature allowing direct communication with the Recovery Handler via the AXI bus. The bypass disables the I3C communication logic. Data is routed from the TTI TX Queue to the Recovery Executor block, and written directly to the Indirect Data FIFO. The Caliptra ROM can access the data from the Indirect FIFO over the AXI bus (the same way it does in the regular I3C recovery flow). Data flow in bypass mode, marked with green arrows, is depicted in the diagram below.
:::{figure-md} recovery_handler_with_bypass

Recovery Handler with the I3C Core logic bypass :::
Secure Firmware Recovery CSRs
With the bypass feature enabled, the FIFO status CSRs in the Secure Firmware Recovery CSR file will be updated by the Recovery Handler module.
However, some registers like e.g. INDIRECT_FIFO_CTRL which are updated by I3C commands in a standard recovery flow, will have to be accessed and configured properly from the software running on the Caliptra MCU via the AXI bus.
All configurable registers are writable from software, read only registers provide status information about Recovery Handler internals, e.g. details about size and fill level of the Indirect FIFO.
be6a343
Register descriptions
This chapter provides auto-generated register descriptions for the core.
I3CCSR address map
- Absolute Address: 0x0
- Base Offset: 0x0
- Size: 0x26C
| Offset | Identifier | Name |
|---|---|---|
| 0x100 | I3C_EC | Extended Capabilities |
I3C_EC register file
- Absolute Address: 0x100
- Base Offset: 0x100
- Size: 0x16C
| Offset | Identifier | Name |
|---|---|---|
| 0x000 | SecFwRecoveryIf | Secure Firmware Recovery Interface |
| 0x080 | StdbyCtrlMode | Standby Controller Mode |
| 0x0C0 | TTI | Target Transaction Interface |
| 0x100 | SoCMgmtIf | SoC Management Interface |
| 0x160 | CtrlCfg | Controller Config |
| 0x168 | TERMINATION_EXTCAP_HEADER | β |
SecFwRecoveryIf register file
- Absolute Address: 0x100
- Base Offset: 0x0
- Size: 0x6C
| Offset | Identifier | Name |
|---|---|---|
| 0x00 | EXTCAP_HEADER | β |
| 0x04 | PROT_CAP_0 | Recovery Protocol Capabilities 0 |
| 0x08 | PROT_CAP_1 | Recovery Protocol Capabilities 1 |
| 0x0C | PROT_CAP_2 | Recovery Protocol Capabilities 2 |
| 0x10 | PROT_CAP_3 | Recovery Protocol Capabilities 3 |
| 0x14 | DEVICE_ID_0 | Device Identification 0 |
| 0x18 | DEVICE_ID_1 | Device Identification 1 |
| 0x1C | DEVICE_ID_2 | Device Identification 2 |
| 0x20 | DEVICE_ID_3 | Device Identification 3 |
| 0x24 | DEVICE_ID_4 | Device Identification 4 |
| 0x28 | DEVICE_ID_5 | Device Identification 5 |
| 0x2C | DEVICE_ID_RESERVED | Reserved |
| 0x30 | DEVICE_STATUS_0 | Device status 0 |
| 0x34 | DEVICE_STATUS_1 | Device status 1 |
| 0x38 | DEVICE_RESET | Reset control |
| 0x3C | RECOVERY_CTRL | Recovery configuration/control |
| 0x40 | RECOVERY_STATUS | Recovery status |
| 0x44 | HW_STATUS | Hardware status |
| 0x48 | INDIRECT_FIFO_CTRL_0 | Indirect FIFO Control 0 |
| 0x4C | INDIRECT_FIFO_CTRL_1 | Indirect FIFO Control 1 |
| 0x50 | INDIRECT_FIFO_STATUS_0 | Indirect FIFO Status 0 |
| 0x54 | INDIRECT_FIFO_STATUS_1 | Indirect FIFO Status 1 |
| 0x58 | INDIRECT_FIFO_STATUS_2 | Indirect FIFO Status 2 |
| 0x5C | INDIRECT_FIFO_STATUS_3 | Indirect FIFO Status 3 |
| 0x60 | INDIRECT_FIFO_STATUS_4 | Indirect FIFO Status 4 |
| 0x64 | INDIRECT_FIFO_RESERVED | INDIRECT_FIFO_RESERVED |
| 0x68 | INDIRECT_FIFO_DATA | INDIRECT_FIFO_DATA |
EXTCAP_HEADER register
- Absolute Address: 0x100
- Base Offset: 0x0
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | CAP_ID | r | 0xC0 | CAP_ID |
| 23:8 | CAP_LENGTH | r | 0x20 | CAP_LENGTH |
CAP_ID field
Extended Capability ID
CAP_LENGTH field
Capability Structure Length in DWORDs
PROT_CAP_0 register
- Absolute Address: 0x104
- Base Offset: 0x4
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | REC_MAGIC_STRING_0 | r | 0x2050434F | Recovery protocol magic string |
REC_MAGIC_STRING_0 field
Magic string 'OCP ' (1st part of 'OCP RECV') in ASCII code - '0x4f 0x43 0x50 0x20'
PROT_CAP_1 register
- Absolute Address: 0x108
- Base Offset: 0x8
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | REC_MAGIC_STRING_1 | r | 0x56434552 | Recovery protocol magic string |
REC_MAGIC_STRING_1 field
Magic string 'RECV' (2nd part of 'OCP RECV') in ASCII code - '0x52 0x45 0x43 0x56'
PROT_CAP_2 register
- Absolute Address: 0x10C
- Base Offset: 0xC
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 15:0 | REC_PROT_VERSION | rw | 0x0 | Recovery protocol version |
| 31:16 | AGENT_CAPS | rw | 0x0 | Recovery protocol capabilities |
REC_PROT_VERSION field
-
Byte 0: Major version number = 0x1
-
Byte 1: Minor version number = 0x1
AGENT_CAPS field
Agent capabilities:
-
bit 0: Identification (DEVICE_ID structure)
-
bit 1: Forced Recovery (From RESET)
-
bit 2: Mgmt reset (From RESET)
-
bit 3: Device Reset (From RESET)
-
bit 4: Device status (DEVICE_STATUS)
-
bit 5: Recovery memory access (INDIRECT_CTRL)
-
bit 6: Local C-image support
-
bit 7: Push C-image support
-
bit 8: Interface isolation
-
bit 9: Hardware status
-
bit 10: Vendor command
-
bit 11: Flashless boot (From RESET)
-
bit 12: FIFO CMS support (INDIRECT_FIFO_CTRL)
-
bits 13-15: Reserved
PROT_CAP_3 register
- Absolute Address: 0x110
- Base Offset: 0x10
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | NUM_OF_CMS_REGIONS | rw | 0x0 | Total number of CMS regions |
| 15:8 | MAX_RESP_TIME | rw | 0x0 | Maximum Response Time |
| 23:16 | HEARTBEAT_PERIOD | rw | 0x0 | Heartbeat Period |
NUM_OF_CMS_REGIONS field
0-255: The total number of component memory space (CMS) regions a device supports. This number includes any logging, code and vendor defined regions
MAX_RESP_TIME field
0-255: Maximum response time in 2^x microseconds(us).
HEARTBEAT_PERIOD field
0-255: Heartbeat period, 2^x microseconds (us), 0 indicates not supported
DEVICE_ID_0 register
- Absolute Address: 0x114
- Base Offset: 0x14
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | DESC_TYPE | rw | 0x0 | Initial descriptor type |
| 15:8 | VENDOR_SPECIFIC_STR_LENGTH | rw | 0x0 | Vendor Specific String Length |
| 31:16 | DATA | rw | 0x0 |
DESC_TYPE field
Based on table 8 from [DMTF PLDM FM]:
-
0x00: PCI Vendor
-
0x1: IANA
-
0x2: UUID
-
0x3: PnP Vendor
-
0x4: ACPI Vendor
-
0x5: IANA Enterprise Type
-
0x6-0xFE: Reserved
-
0xFF: NVMe-MI
VENDOR_SPECIFIC_STR_LENGTH field
0x0-0xFF: Total length of Vendor Specific String, 0 indicates not supported
DATA field
DEVICE_ID_1 register
- Absolute Address: 0x118
- Base Offset: 0x18
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | DATA | rw | 0x0 |
DATA field
DEVICE_ID_2 register
- Absolute Address: 0x11C
- Base Offset: 0x1C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | DATA | rw | 0x0 |
DATA field
DEVICE_ID_3 register
- Absolute Address: 0x120
- Base Offset: 0x20
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | DATA | rw | 0x0 |
DATA field
DEVICE_ID_4 register
- Absolute Address: 0x124
- Base Offset: 0x24
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | DATA | rw | 0x0 |
DATA field
DEVICE_ID_5 register
- Absolute Address: 0x128
- Base Offset: 0x28
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | DATA | rw | 0x0 |
DATA field
DEVICE_ID_RESERVED register
- Absolute Address: 0x12C
- Base Offset: 0x2C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | DATA | r | 0x0 |
DATA field
DEVICE_STATUS_0 register
- Absolute Address: 0x130
- Base Offset: 0x30
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | DEV_STATUS | rw | 0x0 | Device status |
| 15:8 | PROT_ERROR | rw, rclr | 0x0 | Protocol Error |
| 31:16 | REC_REASON_CODE | rw | 0x0 | Recovery Reason Codes |
DEV_STATUS field
-
0x0: Status Pending (Recover Reason Code not populated)
-
0x1: Device healthy (Recover Reason Code not populated)
-
0x2: Device Error (βsoftβ error or other error state) - (Recover Reason Code not populated)
-
0x3: Recovery mode - ready to accept recovery image - (Recover Reason Code populated)
-
0x4: Recovery Pending (waiting for activation) - (Recover Reason Code populated)
-
0x5: Running Recovery Image ( Recover Reason Code not populated)
-
0x6-0xD: Reserved
-
0xE: Boot Failure (Recover Reason Code populated)
-
0xF: Fatal Error (Recover Reason Code not populated)
-
0x10-FF:Reserved
PROT_ERROR field
-
0x0: No Protocol Error
-
0x1: Unsupported/Write Command - command is not support or a write to a RO command
-
0x2: Unsupported Parameter
-
0x3: Length write error (length of write command is incorrect)
-
0x4: CRC Error (if supported)
-
0x5-0xFE: Reserved
-
0xFF: General Protocol Error - catch all unclassified errors
REC_REASON_CODE field
-
0x0: No Boot Failure detected (BFNF)
-
0x1: Generic hardware error (BFGHWE)
-
0x2: Generic hardware soft error (BFGSE) - soft error may be recoverable
-
0x3: Self-test failure (BFSTF) (e.g., RSA self test failure, FIPs self test failure,, etc.)
-
0x4: Corrupted/missing critical data (BFCD)
-
0x5: Missing/corrupt key manifest (BFKMMC)
-
0x6: Authentication Failure on key manifest (BFKMAF)
-
0x7: Anti-rollback failure on key manifest (BFKIAR)
-
0x8: Missing/corrupt boot loader (first mutable code) firmware image (BFFIMC)
-
0x9: Authentication failure on boot loader ( 1st mutable code) firmware image (BFFIAF)
-
0xA: Anti-rollback failure boot loader (1st mutable code) firmware image (BFFIAR)
-
0xB: Missing/corrupt main/management firmware image (BFMFMC)
-
0xC: Authentication Failure main/management firmware image (BFMFAF)
-
0xD: Anti-rollback Failure main/management firmware image (BFMFAR)
-
0xE: Missing/corrupt recovery firmware (BFRFMC)
-
0xF: Authentication Failure recovery firmware (BFRFAF)
-
0x10: Anti-rollback Failure on recovery firmware (BFRFAR)
-
0x11: Forced Recovery (FR)
-
0x12: Flashless/Streaming Boot (FSB)
-
0x13-0x7F: Reserved
-
0x80-0xFF: Vendor Unique Boot Failure Codes
DEVICE_STATUS_1 register
- Absolute Address: 0x134
- Base Offset: 0x34
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 15:0 | HEARTBEAT | rw | 0x0 | Heartbeat |
| 24:16 | VENDOR_STATUS_LENGTH | rw | 0x0 | Vendor Status Length |
| 31:25 | VENDOR_STATUS | rw | 0x0 | Vendor defined status message |
HEARTBEAT field
0-4095: Incrementing number (counter wraps)
VENDOR_STATUS_LENGTH field
0-248: Length in bytes of just VENDOR_STATUS. Zero indicates no vendor status and zero additional bytes.
DEVICE_RESET register
- Absolute Address: 0x138
- Base Offset: 0x38
- Size: 0x4
For devices which support reset, this register will reset the device or management entity
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | RESET_CTRL | rw, woclr | 0x0 | Device Reset Control |
| 15:8 | FORCED_RECOVERY | rw | 0x0 | Forced Recovery |
| 23:16 | IF_CTRL | rw | 0x0 | Interface Control |
RESET_CTRL field
-
0x0: No reset
-
0x1: Reset Device (PCIe Fundamental Reset or equivalent. This is likely bus disruptive)
-
0x2: Reset Management. This reset will reset the management subsystem. If supported, this reset MUST not be bus disruptive (cause re-enumeration)
-
0x3-FF: Reserved
FORCED_RECOVERY field
-
0x0: No forced recovery
-
0x01-0xD: Reserved
-
0xE: Enter flashless boot mode on next platform reset
-
0xF: Enter recovery mode on next platform reset
-
0x10-FF: Reserved
IF_CTRL field
-
0x0: Disable Interface mastering
-
0x1: Enable Interface mastering
RECOVERY_CTRL register
- Absolute Address: 0x13C
- Base Offset: 0x3C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | CMS | rw | 0x0 | Component Memory Space (CMS) |
| 15:8 | REC_IMG_SEL | rw | 0x0 | Recovery Image Selection |
| 23:16 | ACTIVATE_REC_IMG | rw, woclr | 0x0 | Activate Recovery Image |
CMS field
- 0-255: Selects a component memory space where the recovery image is. 0 is the default
REC_IMG_SEL field
-
0x0: No operation
-
0x1: Use Recovery Image from memory window (CMS)
-
0x2: Use Recovery Image stored on device (C-image)
-
0x3-FF: reserved
ACTIVATE_REC_IMG field
-
0x0: do not activate recovery image - after activation device will report this code.
-
0xF: Activate recovery image
-
0x10-FF-reserved
RECOVERY_STATUS register
- Absolute Address: 0x140
- Base Offset: 0x40
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 3:0 | DEV_REC_STATUS | rw | 0x0 | Device recovery status |
| 7:4 | REC_IMG_INDEX | rw | 0x0 | Recovery image index |
| 15:8 | VENDOR_SPECIFIC_STATUS | rw | 0x0 | Vendor specific status |
DEV_REC_STATUS field
-
0x0: Not in recovery mode
-
0x1: Awaiting recovery image
-
0x2: Booting recovery image
-
0x3: Recovery successful
-
0xc: Recovery failed
-
0xd: Recovery image authentication error
-
0xe: Error entering Recovery mode (might be administratively disabled)
-
0xf: Invalid component address space
HW_STATUS register
- Absolute Address: 0x144
- Base Offset: 0x44
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | TEMP_CRITICAL | rw | 0x0 | Device temperature critical |
| 1 | SOFT_ERR | rw | 0x0 | Hardware Soft Error |
| 2 | FATAL_ERR | rw | 0x0 | Hardware Fatal Error |
| 7:3 | RESERVED_7_3 | rw | 0x0 | Reserved |
| 15:8 | VENDOR_HW_STATUS | rw | 0x0 | Vendor HW Status (bit mask active high) |
| 23:16 | CTEMP | rw | 0x0 | Composite temperature (CTemp) |
| 31:24 | VENDOR_HW_STATUS_LEN | rw | 0x0 | Vendor Specific Hardware Status length (bytes) |
TEMP_CRITICAL field
Device temperature is critical (may need reset to clear)
SOFT_ERR field
Hardware Soft Error (may need reset to clear)
CTEMP field
Current temperatureof device in degrees Celsius: Compatible with NVMe-MI command code 0 offset 3.
-
0x00-0x7e: 0 to 126 C
-
0x7f: 127 C or higher
-
0x80: no temperature data, or data is older than 5 seconds
-
0x81: temperature sensor failure
-
0x82-0x83: reserved
-
0xc4: -60 C or lower
-
0xc5-0xff: -59 to -1 C (in two's complement)
VENDOR_HW_STATUS_LEN field
0-251: Length in bytes of Vendor Specific Hardware Status.
INDIRECT_FIFO_CTRL_0 register
- Absolute Address: 0x148
- Base Offset: 0x48
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | CMS | rw | 0x0 | Indirect FIFO memory access configuration. |
| 15:8 | RESET | rw | 0x0 | Indirect memory configuration - reset |
CMS field
This register selects a region within the device. Read/write access is through address spaces. Each space represents a FIFO. Component Memory Space (CMS):
- 0-255: Address region within a device.
RESET field
Reset (Write 1 Clear):
-
0x0: idle
-
0x1: reset Write Index and Read Index to initial value.
-
0x2 to 0xFF: reserved
INDIRECT_FIFO_CTRL_1 register
- Absolute Address: 0x14C
- Base Offset: 0x4C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | IMAGE_SIZE | rw | 0x0 | Indirect memory configuration - Image Size |
IMAGE_SIZE field
Image Size: Size of the image to be loaded in 4B units
INDIRECT_FIFO_STATUS_0 register
- Absolute Address: 0x150
- Base Offset: 0x50
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | EMPTY | r | 0x1 | FIFO Empty |
| 1 | FULL | r | 0x0 | FIFO Full |
| 10:8 | REGION_TYPE | r | 0x0 | Memory Region Type |
EMPTY field
If set, FIFO is empty
FULL field
If set, FIFO is full
REGION_TYPE field
Memory Region Type:
-
0b000: Code space for recovery. (write only)
-
0b001: Log uses the defined debug format (read only)
-
0b100: Vendor Defined Region (write only)
-
0b101: Vendor Defined Region (read only)
-
0b111: Unsupported Region (address space out of range)
INDIRECT_FIFO_STATUS_1 register
- Absolute Address: 0x154
- Base Offset: 0x54
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | WRITE_INDEX | r | 0x0 | FIFO Write Index |
WRITE_INDEX field
Offset incremented for each access by the Recovery Agent in 4B units
INDIRECT_FIFO_STATUS_2 register
- Absolute Address: 0x158
- Base Offset: 0x58
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | READ_INDEX | r | 0x0 | FIFO Read Index |
READ_INDEX field
Offset incremented for each access by the device in 4B units
INDIRECT_FIFO_STATUS_3 register
- Absolute Address: 0x15C
- Base Offset: 0x5C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | FIFO_SIZE | r | 0x40 | Indirect FIFO size |
FIFO_SIZE field
Size of memory window specified in 4B units. Current implementation supports only a constant size of 64.
INDIRECT_FIFO_STATUS_4 register
- Absolute Address: 0x160
- Base Offset: 0x60
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | MAX_TRANSFER_SIZE | r | 0x40 | Max transfer size |
MAX_TRANSFER_SIZE field
Max size of the data payload in each read/write to INDIRECT_FIFO_DATA in 4B units
Enforced to 256 bytes (64 DWORDs) by Caliptra Subsystem Recovery Sequence
INDIRECT_FIFO_RESERVED register
- Absolute Address: 0x164
- Base Offset: 0x64
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | DATA | r | 0x0 | Reserved register |
INDIRECT_FIFO_DATA register
- Absolute Address: 0x168
- Base Offset: 0x68
- Size: 0x4
Indirect memory access to address space configured in INDIRECT_FIFO_CTRL at the Head Pointer offset.
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | DATA | r | 0x0 | β |
StdbyCtrlMode register file
- Absolute Address: 0x180
- Base Offset: 0x80
- Size: 0x40
| Offset | Identifier | Name |
|---|---|---|
| 0x00 | EXTCAP_HEADER | β |
| 0x04 | STBY_CR_CONTROL | Standby Controller Control |
| 0x08 | STBY_CR_DEVICE_ADDR | Standby Controller Device Address |
| 0x0C | STBY_CR_CAPABILITIES | Standby Controller Capabilities |
| 0x10 | STBY_CR_VIRTUAL_DEVICE_CHAR | Standby Controller Virtual Device Characteristics |
| 0x14 | STBY_CR_STATUS | Standby Controller Status |
| 0x18 | STBY_CR_DEVICE_CHAR | Standby Controller Device Characteristics |
| 0x1C | STBY_CR_DEVICE_PID_LO | Standby Controller Device PID Low |
| 0x20 | STBY_CR_INTR_STATUS | Standby Controller Interrupt Status |
| 0x24 | STBY_CR_VIRTUAL_DEVICE_PID_LO | Standby Controller Virtual Device PID Low |
| 0x28 | STBY_CR_INTR_SIGNAL_ENABLE | Standby Controller Interrupt Signal Enable |
| 0x2C | STBY_CR_INTR_FORCE | Standby Controller Interrupt Force |
| 0x30 | STBY_CR_CCC_CONFIG_GETCAPS | Standby Controller CCC Configuration GETCAPS |
| 0x34 | STBY_CR_CCC_CONFIG_RSTACT_PARAMS | Standby Controller CCC Configuration RSTACT |
| 0x38 | STBY_CR_VIRT_DEVICE_ADDR | Standby Virtual Controller Device Address |
| 0x3C | __rsvd_3 | Reserved 3 |
EXTCAP_HEADER register
- Absolute Address: 0x180
- Base Offset: 0x0
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | CAP_ID | r | 0x12 | CAP_ID |
| 23:8 | CAP_LENGTH | r | 0x10 | CAP_LENGTH |
CAP_ID field
Extended Capability ID
CAP_LENGTH field
Capability Structure Length in DWORDs
STBY_CR_CONTROL register
- Absolute Address: 0x184
- Base Offset: 0x4
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | PENDING_RX_NACK | rw | β | Pending RX NACK |
| 1 | HANDOFF_DELAY_NACK | rw | β | Handoff Delay NACK |
| 2 | ACR_FSM_OP_SELECT | rw | β | Active Controller Select |
| 3 | PRIME_ACCEPT_GETACCCR | rw | β | Prime to Accept Controller Role |
| 4 | HANDOFF_DEEP_SLEEP | rw, wset | 0x0 | Handoff Deep Sleep |
| 5 | CR_REQUEST_SEND | w | 0x0 | Send Controller Role Request |
| 10:8 | BAST_CCC_IBI_RING | rw | 0x0 | Ring Bundle IBI Selector for Broadcast CCC Capture |
| 12 | TARGET_XACT_ENABLE | rw | 0x1 | Target Transaction Interface Servicing Enable |
| 13 | DAA_SETAASA_ENABLE | rw | 0x0 | Dynamic Address Method Enable SETAASA |
| 14 | DAA_SETDASA_ENABLE | rw | 0x0 | Dynamic Address Method Enable SETDASA |
| 15 | DAA_ENTDAA_ENABLE | rw | 0x0 | Dynamic Address Method Enable ENTDAA |
| 20 | RSTACT_DEFBYTE_02 | rw | 0x0 | RSTACT Support DefByte 0x02 |
| 31:30 | STBY_CR_ENABLE_INIT | rw | 0x0 | Host Controller Secondary Controller Enable |
PENDING_RX_NACK field
HANDOFF_DELAY_NACK field
ACR_FSM_OP_SELECT field
PRIME_ACCEPT_GETACCCR field
HANDOFF_DEEP_SLEEP field
If this field has a value of 1'b1, then the Secondary Controller Logic shall report a return from Deep Sleep state to the Active Controller. Writing 1'b1 to this bit is sticky. This field shall automatically clear to 1'b0 after accepting the Controller Role and transitioning to Active Controller mode.
CR_REQUEST_SEND field
Write of 1'b1 to this field shall instruct the Secondary Controller Logic to attempt to send a Controller Role Request to the I3C Bus.
BAST_CCC_IBI_RING field
Indicates which Ring Bundle will be used to capture Broadcast CCC data sent by the Active Controller. The Ring Bundle must be configured and enabled, and its IBI Ring Pair must also be initialized and ready to receive data.
TARGET_XACT_ENABLE field
Indicates whether Read-Type/Write-Type transaction servicing is enabled, via an I3C Target Transaction Interface to software (Section 6.17.3).
1'b0: DISABLED: not available
1'b1: ENABLED: available for software
DAA_SETAASA_ENABLE field
Indicates SETAASA method is enabled.
1'b0: DISABLED: will not respond
1'b1: ENABLED: will respond
DAA_SETDASA_ENABLE field
Indicates SETDASA method is enabled.
1'b0: DISABLED: will not respond
1'b1: ENABLED: will respond
DAA_ENTDAA_ENABLE field
Indicates ENTDAA method is enabled.
1'b0: DISABLED: will not respond
1'b1: ENABLED: will respond
RSTACT_DEFBYTE_02 field
Controls whether I3C Secondary Controller Logic supports RSTACT CCC with Defining Byte 0x02.
1'b0: NOT_SUPPORTED: Do not ACK Defining Byte 0x02
1'b1: HANDLE_INTR: Support Defining Byte 0x02
STBY_CR_ENABLE_INIT field
Enables or disables the Secondary Controller:
2'b00 - DISABLED: Secondary Controller is disabled.
2'b01 - ACM_INIT: Secondary Controller is enabled, but Host Controller initializes in Active Controller mode.
2'b10 - SCM_RUNNING: Secondary Controller operation is enabled, Host Controller initializes in Standby Controller mode.
2'b11 - SCM_HOT_JOIN: Secondary Controller operation is enabled, Host Controller conditionally becomes a Hot-Joining Device to receive its Dynamic Address before operating in Standby Controller mode.
STBY_CR_DEVICE_ADDR register
- Absolute Address: 0x188
- Base Offset: 0x8
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 6:0 | STATIC_ADDR | rw | 0x0 | Device Static Address |
| 15 | STATIC_ADDR_VALID | rw | 0x0 | Static Address is Valid |
| 22:16 | DYNAMIC_ADDR | rw | 0x0 | Device Dynamic Address |
| 31 | DYNAMIC_ADDR_VALID | rw | 0x0 | Dynamic Address is Valid |
STATIC_ADDR field
This field contains the Host Controller Deviceβs Static Address.
STATIC_ADDR_VALID field
Indicates whether or not the value in the STATIC_ADDR field is valid.
1'b0: The Static Address field is not valid
1'b1: The Static Address field is valid
DYNAMIC_ADDR field
Contains the Host Controller Deviceβs Dynamic Address.
DYNAMIC_ADDR_VALID field
Indicates whether or not the value in the DYNAMIC_ADDR field is valid. 1'b0: DYNAMIC_ADDR field is not valid 1'b1: DYNAMIC_ADDR field is valid
STBY_CR_CAPABILITIES register
- Absolute Address: 0x18C
- Base Offset: 0xC
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 5 | SIMPLE_CRR_SUPPORT | r | 0x0 | SIMPLE_CRR_SUPPORT |
| 12 | TARGET_XACT_SUPPORT | r | 0x1 | TARGET_XACT_SUPPORT |
| 13 | DAA_SETAASA_SUPPORT | r | 0x1 | DAA_SETAASA_SUPPORT |
| 14 | DAA_SETDASA_SUPPORT | r | 0x1 | DAA_SETDASA_SUPPORT |
| 15 | DAA_ENTDAA_SUPPORT | r | 0x0 | DAA_ENTDAA_SUPPORT |
SIMPLE_CRR_SUPPORT field
TARGET_XACT_SUPPORT field
Defines whether an I3C Target Transaction Interface is supported.
1'b0: DISABLED: Not supported
1'b1: ENABLED: Supported via vendor-defined Extended Capability structure
DAA_SETAASA_SUPPORT field
Defines whether Dynamic Address Assignment with SETAASA CCC (using Static Address) is supported.
1'b0: DISABLED: Not supported
1'b1: ENABLED: Supported
DAA_SETDASA_SUPPORT field
Defines whether Dynamic Address Assignment with SETDASA CCC (using Static Address) is supported.
1'b0: DISABLED: Not supported
1'b1: ENABLED: Supported
DAA_ENTDAA_SUPPORT field
Defines whether Dynamic Address Assignment with ENTDAA CCC is supported.
1'b0: DISABLED: Not supported
1'b1: ENABLED: Supported
STBY_CR_VIRTUAL_DEVICE_CHAR register
- Absolute Address: 0x190
- Base Offset: 0x10
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 15:1 | PID_HI | rw | 0x7FFF | PID_HI |
| 23:16 | DCR | rw | 0xBD | DCR |
| 28:24 | BCR_VAR | rw | 0x16 | BCR_VAR |
| 31:29 | BCR_FIXED | rw | 0x1 | BCR_FIXED |
PID_HI field
High part of the 48-bit Target Device Provisioned ID.
DCR field
Device Characteristics Register. Value represents an OCP Recovery Device.
BCR_VAR field
Bus Characteristics, Variable Part.
Reset value is set to 5'b10110, because this device:
-
[bit4] is a Virtual Target
-
[bit3] is not Offline Capable
-
[bit2] uses the MDB in the IBI Payload
-
[bit1] is capable of IBI requests
-
[bit0] has no speed limitation
BCR_FIXED field
Bus Characteristics, Fixed Part.
Reset value is set to 3'b001, because this device is an I3C Target, which supports extended capabilities
STBY_CR_STATUS register
- Absolute Address: 0x194
- Base Offset: 0x14
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 2 | AC_CURRENT_OWN | rw | β | AC_CURRENT_OWN |
| 7:5 | SIMPLE_CRR_STATUS | rw | β | SIMPLE_CRR_STATUS |
| 8 | HJ_REQ_STATUS | rw | β | HJ_REQ_STATUS |
AC_CURRENT_OWN field
SIMPLE_CRR_STATUS field
HJ_REQ_STATUS field
STBY_CR_DEVICE_CHAR register
- Absolute Address: 0x198
- Base Offset: 0x18
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 15:1 | PID_HI | rw | 0x7FFF | PID_HI |
| 23:16 | DCR | rw | 0xBD | DCR |
| 28:24 | BCR_VAR | rw | 0x6 | BCR_VAR |
| 31:29 | BCR_FIXED | rw | 0x1 | BCR_FIXED |
PID_HI field
High part of the 48-bit Target Device Provisioned ID.
DCR field
Device Characteristics Register. Value represents an OCP Recovery Device.
BCR_VAR field
Bus Characteristics, Variable Part.
Reset value is set to 5'b00110, because this device:
-
[bit4] is not a Virtual Target
-
[bit3] is not Offline Capable
-
[bit2] uses the MDB in the IBI Payload
-
[bit1] is capable of IBI requests
-
[bit0] has no speed limitation
BCR_FIXED field
Bus Characteristics, Fixed Part.
Reset value is set to 3'b001, because this device is an I3C Target, which supports extended capabilities
STBY_CR_DEVICE_PID_LO register
- Absolute Address: 0x19C
- Base Offset: 0x1C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | PID_LO | rw | 0x5A00A5 | PID_LO |
PID_LO field
Low part of the 48-bit Target Device Provisioned ID.
STBY_CR_INTR_STATUS register
- Absolute Address: 0x1A0
- Base Offset: 0x20
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | ACR_HANDOFF_OK_REMAIN_STAT | rw | β | |
| 1 | ACR_HANDOFF_OK_PRIMED_STAT | rw | β | |
| 2 | ACR_HANDOFF_ERR_FAIL_STAT | rw | β | |
| 3 | ACR_HANDOFF_ERR_M3_STAT | rw | β | |
| 10 | CRR_RESPONSE_STAT | rw | β | |
| 11 | STBY_CR_DYN_ADDR_STAT | rw | β | |
| 12 | STBY_CR_ACCEPT_NACKED_STAT | rw | β | |
| 13 | STBY_CR_ACCEPT_OK_STAT | rw | β | |
| 14 | STBY_CR_ACCEPT_ERR_STAT | rw | β | |
| 16 | STBY_CR_OP_RSTACT_STAT | rw | 0x0 | Secondary Controller Operation Reset Action |
| 17 | CCC_PARAM_MODIFIED_STAT | rw | β | |
| 18 | CCC_UNHANDLED_NACK_STAT | rw | β | |
| 19 | CCC_FATAL_RSTDAA_ERR_STAT | rw | β |
ACR_HANDOFF_OK_REMAIN_STAT field
ACR_HANDOFF_OK_PRIMED_STAT field
ACR_HANDOFF_ERR_FAIL_STAT field
ACR_HANDOFF_ERR_M3_STAT field
CRR_RESPONSE_STAT field
STBY_CR_DYN_ADDR_STAT field
STBY_CR_ACCEPT_NACKED_STAT field
STBY_CR_ACCEPT_OK_STAT field
STBY_CR_ACCEPT_ERR_STAT field
STBY_CR_OP_RSTACT_STAT field
The Host Controller shall write 1'b1 to this field to indicate that the Secondary Controller received a RSTACT CCC from the Active Controller, followed by the Target Reset Pattern.
CCC_PARAM_MODIFIED_STAT field
CCC_UNHANDLED_NACK_STAT field
CCC_FATAL_RSTDAA_ERR_STAT field
STBY_CR_VIRTUAL_DEVICE_PID_LO register
- Absolute Address: 0x1A4
- Base Offset: 0x24
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | PID_LO | rw | 0x5A10A5 | PID_LO |
PID_LO field
Low part of the 48-bit Target Virtual Device Provisioned ID.
STBY_CR_INTR_SIGNAL_ENABLE register
- Absolute Address: 0x1A8
- Base Offset: 0x28
- Size: 0x4
When set to 1'b1, and the corresponding interrupt status field is set in register STBY_CR_INTR_STATUS, the Host Controller shall assert an interrupt to the Host.
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | ACR_HANDOFF_OK_REMAIN_SIGNAL_EN | rw | β | |
| 1 | ACR_HANDOFF_OK_PRIMED_SIGNAL_EN | rw | β | |
| 2 | ACR_HANDOFF_ERR_FAIL_SIGNAL_EN | rw | β | |
| 3 | ACR_HANDOFF_ERR_M3_SIGNAL_EN | rw | β | |
| 10 | CRR_RESPONSE_SIGNAL_EN | rw | β | |
| 11 | STBY_CR_DYN_ADDR_SIGNAL_EN | rw | β | |
| 12 | STBY_CR_ACCEPT_NACKED_SIGNAL_EN | rw | β | |
| 13 | STBY_CR_ACCEPT_OK_SIGNAL_EN | rw | β | |
| 14 | STBY_CR_ACCEPT_ERR_SIGNAL_EN | rw | β | |
| 16 | STBY_CR_OP_RSTACT_SIGNAL_EN | rw | 0x0 | |
| 17 | CCC_PARAM_MODIFIED_SIGNAL_EN | rw | β | |
| 18 | CCC_UNHANDLED_NACK_SIGNAL_EN | rw | β | |
| 19 | CCC_FATAL_RSTDAA_ERR_SIGNAL_EN | rw | β |
ACR_HANDOFF_OK_REMAIN_SIGNAL_EN field
ACR_HANDOFF_OK_PRIMED_SIGNAL_EN field
ACR_HANDOFF_ERR_FAIL_SIGNAL_EN field
ACR_HANDOFF_ERR_M3_SIGNAL_EN field
CRR_RESPONSE_SIGNAL_EN field
STBY_CR_DYN_ADDR_SIGNAL_EN field
STBY_CR_ACCEPT_NACKED_SIGNAL_EN field
STBY_CR_ACCEPT_OK_SIGNAL_EN field
STBY_CR_ACCEPT_ERR_SIGNAL_EN field
STBY_CR_OP_RSTACT_SIGNAL_EN field
CCC_PARAM_MODIFIED_SIGNAL_EN field
CCC_UNHANDLED_NACK_SIGNAL_EN field
CCC_FATAL_RSTDAA_ERR_SIGNAL_EN field
STBY_CR_INTR_FORCE register
- Absolute Address: 0x1AC
- Base Offset: 0x2C
- Size: 0x4
For software testing, when set to 1'b1, forces the corresponding interrupt to be sent to the Host, if the corresponding fields are set in register STBY_CR_INTR_SIGNAL_ENABLE
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 10 | CRR_RESPONSE_FORCE | rw | β | |
| 11 | STBY_CR_DYN_ADDR_FORCE | rw | β | |
| 12 | STBY_CR_ACCEPT_NACKED_FORCE | rw | β | |
| 13 | STBY_CR_ACCEPT_OK_FORCE | rw | β | |
| 14 | STBY_CR_ACCEPT_ERR_FORCE | rw | β | |
| 16 | STBY_CR_OP_RSTACT_FORCE | w | β | |
| 17 | CCC_PARAM_MODIFIED_FORCE | rw | β | |
| 18 | CCC_UNHANDLED_NACK_FORCE | rw | β | |
| 19 | CCC_FATAL_RSTDAA_ERR_FORCE | rw | β |
CRR_RESPONSE_FORCE field
STBY_CR_DYN_ADDR_FORCE field
STBY_CR_ACCEPT_NACKED_FORCE field
STBY_CR_ACCEPT_OK_FORCE field
STBY_CR_ACCEPT_ERR_FORCE field
STBY_CR_OP_RSTACT_FORCE field
CCC_PARAM_MODIFIED_FORCE field
CCC_UNHANDLED_NACK_FORCE field
CCC_FATAL_RSTDAA_ERR_FORCE field
STBY_CR_CCC_CONFIG_GETCAPS register
- Absolute Address: 0x1B0
- Base Offset: 0x30
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 2:0 | F2_CRCAP1_BUS_CONFIG | rw | β | |
| 11:8 | F2_CRCAP2_DEV_INTERACT | rw | β |
F2_CRCAP1_BUS_CONFIG field
F2_CRCAP2_DEV_INTERACT field
STBY_CR_CCC_CONFIG_RSTACT_PARAMS register
- Absolute Address: 0x1B4
- Base Offset: 0x34
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | RST_ACTION | r | 0x0 | Defining Byte of the RSTACT CCC |
| 15:8 | RESET_TIME_PERIPHERAL | rw | 0x0 | Time to Reset Peripheral |
| 23:16 | RESET_TIME_TARGET | rw | 0x0 | Time to Reset Target |
| 31 | RESET_DYNAMIC_ADDR | rw | 0x1 | Reset Dynamic Address after Target Reset |
RST_ACTION field
Contains the Defining Byte received with the last Direct SET CCC sent by the Active Controller.
RESET_TIME_PERIPHERAL field
For Direct GET CCC, this field is returned for Defining Byte 0x81.
RESET_TIME_TARGET field
For Direct GET CCC, this field is returned for Defining Byte 0x82.
RESET_DYNAMIC_ADDR field
If set to 1'b1, then the Secondary Controller Logic must clear its Dynamic Address in register STBY_CR_DEVICE_ADDR after receiving a Target Reset Pattern that followed a Broadcast or Direct SET RSTACT CCC sent to the Dynamic Address, with Defining Byte 0x01 or 0x02. Requires support for Dynamic Address Assignment with at least one supported method, such as the ENTDAA CCC, with field DAA_ENTDAA_ENABLE set to 1'b1 in register STBY_CR_CONTROL. If field ACR_FSM_OP_SELECT in register STBY_CR_CONTROL is set to 1'b1, then this field shall be cleared (i.e., readiness to accept the Controller Role shall be revoked) with this Target Reset Pattern.
STBY_CR_VIRT_DEVICE_ADDR register
- Absolute Address: 0x1B8
- Base Offset: 0x38
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 6:0 | VIRT_STATIC_ADDR | rw | 0x0 | Device Static Address |
| 15 | VIRT_STATIC_ADDR_VALID | rw | 0x0 | Virtual Device Static Address is Valid |
| 22:16 | VIRT_DYNAMIC_ADDR | rw | 0x0 | Virtual Device Dynamic Address |
| 31 | VIRT_DYNAMIC_ADDR_VALID | rw | 0x0 | Virtual Device Dynamic Address is Valid |
VIRT_STATIC_ADDR field
This field contains the Host Controller Deviceβs Static Address.
VIRT_STATIC_ADDR_VALID field
Indicates whether or not the value in the VIRT_STATIC_ADDR field is valid.
1'b0: The Virtual Device Static Address field is not valid
1'b1: The Virtual Device Static Address field is valid
VIRT_DYNAMIC_ADDR field
Contains the Controller Virtual Deviceβs Dynamic Address.
VIRT_DYNAMIC_ADDR_VALID field
Indicates whether or not the value in the VIRT_DYNAMIC_ADDR field is valid. 1'b0: VIRT_DYNAMIC_ADDR field is not valid 1'b1: VIRT_DYNAMIC_ADDR field is valid
__rsvd_3 register
- Absolute Address: 0x1BC
- Base Offset: 0x3C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | __rsvd | rw | β | Reserved |
__rsvd field
TTI register file
- Absolute Address: 0x1C0
- Base Offset: 0xC0
- Size: 0x40
| Offset | Identifier | Name |
|---|---|---|
| 0x00 | EXTCAP_HEADER | β |
| 0x04 | CONTROL | TTI Control |
| 0x08 | STATUS | TTI Status |
| 0x0C | RESET_CONTROL | TTI Queue Reset Control |
| 0x10 | INTERRUPT_STATUS | TTI Interrupt Status |
| 0x14 | INTERRUPT_ENABLE | TTI Interrupt Enable |
| 0x18 | INTERRUPT_FORCE | TTI Interrupt Force |
| 0x1C | RX_DESC_QUEUE_PORT | TTI RX Descriptor Queue Port |
| 0x20 | RX_DATA_PORT | TTI RX Data Port |
| 0x24 | TX_DESC_QUEUE_PORT | TTI TX Descriptor Queue Port |
| 0x28 | TX_DATA_PORT | TTI TX Data Port |
| 0x2C | IBI_PORT | TTI IBI Data Port |
| 0x30 | QUEUE_SIZE | TTI Queue Size |
| 0x34 | IBI_QUEUE_SIZE | TTI IBI Queue Size |
| 0x38 | QUEUE_THLD_CTRL | TTI Queue Threshold Control |
| 0x3C | DATA_BUFFER_THLD_CTRL | TTI IBI Queue Threshold Control |
EXTCAP_HEADER register
- Absolute Address: 0x1C0
- Base Offset: 0x0
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | CAP_ID | r | 0xC4 | CAP_ID |
| 23:8 | CAP_LENGTH | r | 0x10 | CAP_LENGTH |
CAP_ID field
Extended Capability ID
CAP_LENGTH field
Capability Structure Length in DWORDs
CONTROL register
- Absolute Address: 0x1C4
- Base Offset: 0x4
- Size: 0x4
Control Register
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 10 | HJ_EN | rw | 0x1 | HJ_EN |
| 11 | CRR_EN | rw | 0x0 | CRR_EN |
| 12 | IBI_EN | rw | 0x1 | IBI_EN |
| 15:13 | IBI_RETRY_NUM | rw | 0x0 | IBI_RETRY_NUM |
HJ_EN field
Enable Hot-Join capability.
Values:
0x0 - Device is allowed to attempt Hot-Join.
0x1 - Device is not allowed to attempt Hot-Join.
CRR_EN field
Enable Controller Role Request.
Values:
0x0 - Device is allowed to perform Controller Role Request.
0x1 - Device is not allowed to perform Controller Role Request.
IBI_EN field
Enable the IBI queue servicing.
Values:
0x0 - Device will not service the IBI queue.
0x1 - Device will send IBI requests onto the bus, if possible.
IBI_RETRY_NUM field
Number of times the Target Device will try to request an IBI before giving up.
Values:
0x0 - Device will never retry.
0x1-0x6 - Device will retry this many times.
0x7 - Device will retry indefinitely until the Active Controller sets DISINT bit in the DISEC command.
STATUS register
- Absolute Address: 0x1C8
- Base Offset: 0x8
- Size: 0x4
Status Register
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 13 | PROTOCOL_ERROR | r | 0x0 | PROTOCOL_ERROR |
| 15:14 | LAST_IBI_STATUS | r | 0x0 | LAST_IBI_STATUS |
PROTOCOL_ERROR field
Protocol error occurred in the past. This field can only be reset by the Controller, if it issues the GETSTATUS CCC.
Values:
0 - no error occurred
1 - generic protocol error occurred in the past. It will be set until reception of the next GETSTATUS command.
LAST_IBI_STATUS field
Status of last IBI. Should be read after IBI_DONE interrupt.
Values:
00 - Success: IBI was transmitted and ACK'd by the Active Controller. 01 - Failure: Active Controller NACK'd the IBI before any data was sent. The Target Device will retry sending the IBI once. 10 - Failure: Active Controller NACK'd the IBI after partial data was sent. Part of data in the IBI queue is considered corrupted and will be discarded. 11 - Failure: IBI was terminated after 1 retry.
RESET_CONTROL register
- Absolute Address: 0x1CC
- Base Offset: 0xC
- Size: 0x4
Queue Reset Control
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | SOFT_RST | rw | 0x0 | SOFT_RST |
| 1 | TX_DESC_RST | rw | 0x0 | TX_DESC_RST |
| 2 | RX_DESC_RST | rw | 0x0 | RX_DESC_RST |
| 3 | TX_DATA_RST | rw | 0x0 | TX_DATA_RST |
| 4 | RX_DATA_RST | rw | 0x0 | RX_DATA_RST |
| 5 | IBI_QUEUE_RST | rw | 0x0 | IBI_QUEUE_RST |
SOFT_RST field
Target Core Software Reset
TX_DESC_RST field
TTI TX Descriptor Queue Buffer Software Reset
RX_DESC_RST field
TTI RX Descriptor Queue Buffer Software Reset
TX_DATA_RST field
TTI TX Data Queue Buffer Software Reset
RX_DATA_RST field
TTI RX Data Queue Buffer Software Reset
IBI_QUEUE_RST field
TTI IBI Queue Buffer Software Reset
INTERRUPT_STATUS register
- Absolute Address: 0x1D0
- Base Offset: 0x10
- Size: 0x4
Interrupt Status
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | RX_DESC_STAT | rw, woclr | 0x0 | RX_DESC_STAT |
| 1 | TX_DESC_STAT | rw, woclr | 0x0 | TX_DESC_STAT |
| 2 | RX_DESC_TIMEOUT | rw, woclr | 0x0 | RX_DESC_TIMEOUT |
| 3 | TX_DESC_TIMEOUT | rw, woclr | 0x0 | TX_DESC_TIMEOUT |
| 8 | TX_DATA_THLD_STAT | rw, woclr | 0x0 | TX_DATA_THLD_STAT |
| 9 | RX_DATA_THLD_STAT | rw, woclr | 0x0 | RX_DATA_THLD_STAT |
| 10 | TX_DESC_THLD_STAT | rw, woclr | 0x0 | TX_DESC_THLD_STAT |
| 11 | RX_DESC_THLD_STAT | rw, woclr | 0x0 | RX_DESC_THLD_STAT |
| 12 | IBI_THLD_STAT | rw, woclr | 0x0 | IBI_THLD_STAT |
| 13 | IBI_DONE | rw, woclr | 0x0 | IBI_DONE |
| 18:15 | PENDING_INTERRUPT | rw | 0x0 | PENDING_INTERRUPT |
| 25 | TRANSFER_ABORT_STAT | rw, woclr | 0x0 | TRANSFER_ABORT_STAT |
| 26 | TX_DESC_COMPLETE | rw, woclr | 0x0 | TX_DESC_COMPLETE |
| 31 | TRANSFER_ERR_STAT | rw, woclr | 0x0 | TRANSFER_ERR_STAT |
RX_DESC_STAT field
There is a pending Write Transaction. Software should read data from the RX Descriptor Queue and the RX Data Queue
TX_DESC_STAT field
There is a pending Read Transaction on the I3C Bus. Software should write data to the TX Descriptor Queue and the TX Data Queue
RX_DESC_TIMEOUT field
Pending Write was NACKβed, because the RX_DESC_STAT event was not handled in time
TX_DESC_TIMEOUT field
Pending Read was NACKβed, because the TX_DESC_STAT event was not handled in time
TX_DATA_THLD_STAT field
TTI TX Data Buffer Threshold Status, the Target Controller shall set this bit to 1 when the number of available entries in the TTI TX Data Queue is >= the value defined in TTI_TX_DATA_THLD
RX_DATA_THLD_STAT field
TTI RX Data Buffer Threshold Status, the Target Controller shall set this bit to 1 when the number of entries in the TTI RX Data Queue is >= the value defined in TTI_RX_DATA_THLD
TX_DESC_THLD_STAT field
TTI TX Descriptor Buffer Threshold Status, the Target Controller shall set this bit to 1 when the number of available entries in the TTI TX Descriptor Queue is >= the value defined in TTI_TX_DESC_THLD
RX_DESC_THLD_STAT field
TTI RX Descriptor Buffer Threshold Status, the Target Controller shall set this bit to 1 when the number of available entries in the TTI RX Descriptor Queue is >= the value defined in TTI_RX_DESC_THLD
IBI_THLD_STAT field
TTI IBI Buffer Threshold Status, the Target Controller shall set this bit to 1 when the number of available entries in the TTI IBI Queue is >= the value defined in TTI_IBI_THLD
IBI_DONE field
IBI is done, check LAST_IBI_STATUS for result.
PENDING_INTERRUPT field
Contains the interrupt number of any pending interrupt, or 0 if no interrupts are pending. This encoding allows for up to 15 numbered interrupts. If more than one interrupt is set, then the highest priority interrupt shall be returned.
TRANSFER_ABORT_STAT field
Bus aborted transaction
TX_DESC_COMPLETE field
Read Transaction on the I3C Bus completede
TRANSFER_ERR_STAT field
Bus error occurred
INTERRUPT_ENABLE register
- Absolute Address: 0x1D4
- Base Offset: 0x14
- Size: 0x4
Interrupt Enable
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | RX_DESC_STAT_EN | rw | 0x0 | RX_DESC_STAT_EN |
| 1 | TX_DESC_STAT_EN | rw | 0x0 | TX_DESC_STAT_EN |
| 2 | RX_DESC_TIMEOUT_EN | rw | 0x0 | RX_DESC_TIMEOUT_EN |
| 3 | TX_DESC_TIMEOUT_EN | rw | 0x0 | TX_DESC_TIMEOUT_EN |
| 8 | TX_DATA_THLD_STAT_EN | rw | 0x0 | TX_DATA_THLD_STAT_EN |
| 9 | RX_DATA_THLD_STAT_EN | rw | 0x0 | RX_DATA_THLD_STAT_EN |
| 10 | TX_DESC_THLD_STAT_EN | rw | 0x0 | TX_DESC_THLD_STAT_EN |
| 11 | RX_DESC_THLD_STAT_EN | rw | 0x0 | RX_DESC_THLD_STAT_EN |
| 12 | IBI_THLD_STAT_EN | rw | 0x0 | IBI_THLD_STAT_EN |
| 13 | IBI_DONE_EN | rw | 0x0 | IBI_DONE_EN |
| 25 | TRANSFER_ABORT_STAT_EN | rw | 0x0 | TRANSFER_ABORT_STAT_EN |
| 26 | TX_DESC_COMPLETE_EN | rw | 0x0 | TX_DESC_COMPLETE_EN |
| 31 | TRANSFER_ERR_STAT_EN | rw | 0x0 | TRANSFER_ERR_STAT_EN |
RX_DESC_STAT_EN field
Enables the corresponding interrupt bit RX_DESC_STAT_EN
TX_DESC_STAT_EN field
Enables the corresponding interrupt bit TX_DESC_STAT_EN
RX_DESC_TIMEOUT_EN field
Enables the corresponding interrupt bit RX_DESC_TIMEOUT_EN
TX_DESC_TIMEOUT_EN field
Enables the corresponding interrupt bit TX_DESC_TIMEOUT_EN
TX_DATA_THLD_STAT_EN field
Enables the corresponding interrupt bit TTI_TX_DATA_THLD_STAT
RX_DATA_THLD_STAT_EN field
Enables the corresponding interrupt bit TTI_RX_DATA_THLD_STAT
TX_DESC_THLD_STAT_EN field
Enables the corresponding interrupt bit TTI_TX_DESC_THLD_STAT
RX_DESC_THLD_STAT_EN field
Enables the corresponding interrupt bit TTI_RX_DESC_THLD_STAT
IBI_THLD_STAT_EN field
Enables the corresponding interrupt bit TTI_IBI_THLD_STAT
IBI_DONE_EN field
Enables the corresponding interrupt bit IBI_DONE
TRANSFER_ABORT_STAT_EN field
Enables the corresponding interrupt bit TRANSFER_ABORT_STAT
TX_DESC_COMPLETE_EN field
Enables the corresponding interrupt bit TX_DESC_COMPLETE_EN
TRANSFER_ERR_STAT_EN field
Enables the corresponding interrupt bit TRANSFER_ERR_STAT
INTERRUPT_FORCE register
- Absolute Address: 0x1D8
- Base Offset: 0x18
- Size: 0x4
Interrupt Force
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | RX_DESC_STAT_FORCE | rw | 0x0 | RX_DESC_STAT_FORCE |
| 1 | TX_DESC_STAT_FORCE | rw | 0x0 | TX_DESC_STAT_FORCE |
| 2 | RX_DESC_TIMEOUT_FORCE | rw | 0x0 | RX_DESC_TIMEOUT_FORCE |
| 3 | TX_DESC_TIMEOUT_FORCE | rw | 0x0 | TX_DESC_TIMEOUT_FORCE |
| 8 | TX_DATA_THLD_FORCE | rw | 0x0 | TX_DATA_THLD_FORCE |
| 9 | RX_DATA_THLD_FORCE | rw | 0x0 | RX_DATA_THLD_FORCE |
| 10 | TX_DESC_THLD_FORCE | rw | 0x0 | TX_DESC_THLD_FORCE |
| 11 | RX_DESC_THLD_FORCE | rw | 0x0 | RX_DESC_THLD_FORCE |
| 12 | IBI_THLD_FORCE | rw | 0x0 | IBI_THLD_FORCE |
| 13 | IBI_DONE_FORCE | rw | 0x0 | IBI_DONE_FORCE |
| 25 | TRANSFER_ABORT_STAT_FORCE | rw | 0x0 | TRANSFER_ABORT_STAT_FORCE |
| 26 | TX_DESC_COMPLETE_FORCE | rw | 0x0 | TX_DESC_COMPLETE_FORCE |
| 31 | TRANSFER_ERR_STAT_FORCE | rw | 0x0 | TRANSFER_ERR_STAT_FORCE |
RX_DESC_STAT_FORCE field
Enables the corresponding interrupt bit RX_DESC_STAT_FORCE
TX_DESC_STAT_FORCE field
Enables the corresponding interrupt bit TX_DESC_STAT_FORCE
RX_DESC_TIMEOUT_FORCE field
Enables the corresponding interrupt bit RX_DESC_TIMEOUT_FORCE
TX_DESC_TIMEOUT_FORCE field
Enables the corresponding interrupt bit TX_DESC_TIMEOUT_FORCE
TX_DATA_THLD_FORCE field
Forces the corresponding interrupt bit TTI_TX_DATA_THLD_STAT to be set to 1
RX_DATA_THLD_FORCE field
Forces the corresponding interrupt bit TTI_RX_DATA_THLD_STAT to be set to 1
TX_DESC_THLD_FORCE field
Forces the corresponding interrupt bit TTI_TX_DESC_THLD_STAT to be set to 1
RX_DESC_THLD_FORCE field
Forces the corresponding interrupt bit TTI_RX_DESC_THLD_STAT to be set to 1
IBI_THLD_FORCE field
Forces the corresponding interrupt bit TTI_IBI_THLD_STAT to be set to 1
IBI_DONE_FORCE field
Enables the corresponding interrupt bit IBI_DONE_FORCE
TRANSFER_ABORT_STAT_FORCE field
Enables the corresponding interrupt bit TRANSFER_ABORT_STAT_FORCE
TX_DESC_COMPLETE_FORCE field
Enables the corresponding interrupt bit TX_DESC_COMPLETE_FORCE
TRANSFER_ERR_STAT_FORCE field
Enables the corresponding interrupt bit TRANSFER_ERR_STAT_FORCE
RX_DESC_QUEUE_PORT register
- Absolute Address: 0x1DC
- Base Offset: 0x1C
- Size: 0x4
RX Descriptor Queue Port
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | RX_DESC | r | 0x0 | RX_DESC |
RX_DESC field
RX Data
RX_DATA_PORT register
- Absolute Address: 0x1E0
- Base Offset: 0x20
- Size: 0x4
RX Data Port
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | RX_DATA | r | 0x0 | RX_DATA |
RX_DATA field
RX Data
TX_DESC_QUEUE_PORT register
- Absolute Address: 0x1E4
- Base Offset: 0x24
- Size: 0x4
TX Descriptor Queue Port
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | TX_DESC | w | 0x0 | TX_DESC |
TX_DESC field
TX Data
TX_DATA_PORT register
- Absolute Address: 0x1E8
- Base Offset: 0x28
- Size: 0x4
TX Data Port
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | TX_DATA | w | 0x0 | TX_DATA |
TX_DATA field
TX Data
IBI_PORT register
- Absolute Address: 0x1EC
- Base Offset: 0x2C
- Size: 0x4
IBI Data Port
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | IBI_DATA | w | 0x0 | IBI_DATA |
IBI_DATA field
IBI Data
QUEUE_SIZE register
- Absolute Address: 0x1F0
- Base Offset: 0x30
- Size: 0x4
Queue Size
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | RX_DESC_BUFFER_SIZE | r | 0x5 | RX_DESC_BUFFER_SIZE |
| 15:8 | TX_DESC_BUFFER_SIZE | r | 0x5 | TX_DESC_BUFFER_SIZE |
| 23:16 | RX_DATA_BUFFER_SIZE | r | 0x5 | RX_DATA_BUFFER_SIZE |
| 31:24 | TX_DATA_BUFFER_SIZE | r | 0x5 | TX_DATA_BUFFER_SIZE |
RX_DESC_BUFFER_SIZE field
RX Descriptor Buffer Size in DWORDs calculated as 2^(N+1)
TX_DESC_BUFFER_SIZE field
TX Descriptor Buffer Size in DWORDs calculated as 2^(N+1)
RX_DATA_BUFFER_SIZE field
Receive Data Buffer Size in DWORDs calculated as 2^(N+1)
TX_DATA_BUFFER_SIZE field
Transmit Data Buffer Size in DWORDs calculated as 2^(N+1)
IBI_QUEUE_SIZE register
- Absolute Address: 0x1F4
- Base Offset: 0x34
- Size: 0x4
IBI Queue Size
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | IBI_QUEUE_SIZE | r | 0x5 | IBI_QUEUE_SIZE |
IBI_QUEUE_SIZE field
IBI Queue Size in DWORDs calculated as 2^(N+1)
QUEUE_THLD_CTRL register
- Absolute Address: 0x1F8
- Base Offset: 0x38
- Size: 0x4
Queue Threshold Control
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | TX_DESC_THLD | rw | 0x1 | TX_DESC_THLD |
| 15:8 | RX_DESC_THLD | rw | 0x1 | RX_DESC_THLD |
| 31:24 | IBI_THLD | rw | 0x1 | IBI_THLD |
TX_DESC_THLD field
Controls the minimum number of empty TTI TX Descriptor Queue entries needed to trigger the TTI TX Descriptor interrupt.
RX_DESC_THLD field
Controls the minimum number of TTI RX Descriptor Queue entries needed to trigger the TTI RX Descriptor interrupt.
IBI_THLD field
Controls the minimum number of IBI Queue entries needed to trigger the IBI threshold interrupt.
DATA_BUFFER_THLD_CTRL register
- Absolute Address: 0x1FC
- Base Offset: 0x3C
- Size: 0x4
IBI Queue Threshold Control
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 2:0 | TX_DATA_THLD | rw | 0x1 | TX_DATA_THLD |
| 10:8 | RX_DATA_THLD | rw | 0x1 | RX_DATA_THLD |
| 18:16 | TX_START_THLD | rw | 0x1 | TX_DATA_THLD |
| 26:24 | RX_START_THLD | rw | 0x1 | RX_DATA_THLD |
TX_DATA_THLD field
Minimum number of available TTI TX Data queue entries, in DWORDs, that will trigger the TTI TX Data interrupt. Interrupt triggers when 2^(N+1) TX Buffer DWORD entries are available.
RX_DATA_THLD field
Minimum number of TTI RX Data queue entries of data received, in DWORDs, that will trigger the TTI RX Data interrupt. Interrupt triggers when 2^(N+1) RX Buffer DWORD entries are received during the Read transfer.
TX_START_THLD field
Minimum number of available TTI TX Data queue entries, in DWORDs, that will trigger the TTI TX Data interrupt. Interrupt triggers when 2^(N+1) TX Buffer DWORD entries are available.
RX_START_THLD field
Minimum number of TTI RX Data queue entries of data received, in DWORDs, that will trigger the TTI RX Data interrupt. Interrupt triggers when 2^(N+1) RX Buffer DWORD entries are received during the Read transfer.
SoCMgmtIf register file
- Absolute Address: 0x200
- Base Offset: 0x100
- Size: 0x5C
| Offset | Identifier | Name |
|---|---|---|
| 0x00 | EXTCAP_HEADER | β |
| 0x04 | SOC_MGMT_CONTROL | SoC Management Control |
| 0x08 | SOC_MGMT_STATUS | SoC Management Status |
| 0x0C | REC_INTF_CFG | Configuration of Recovery Interface |
| 0x10 | REC_INTF_REG_W1C_ACCESS | Mock hardware register access |
| 0x14 | SOC_MGMT_RSVD_2 | |
| 0x18 | SOC_MGMT_RSVD_3 | |
| 0x1C | SOC_PAD_CONF | I3C Pad Configuration Register |
| 0x20 | SOC_PAD_ATTR | I3C Pad Attribute Configuration Register |
| 0x24 | SOC_MGMT_FEATURE_2 | |
| 0x28 | SOC_MGMT_FEATURE_3 | |
| 0x2C | T_R_REG | |
| 0x30 | T_F_REG | |
| 0x34 | T_SU_DAT_REG | |
| 0x38 | T_HD_DAT_REG | |
| 0x3C | T_HIGH_REG | |
| 0x40 | T_LOW_REG | |
| 0x44 | T_HD_STA_REG | |
| 0x48 | T_SU_STA_REG | |
| 0x4C | T_SU_STO_REG | |
| 0x50 | T_FREE_REG | |
| 0x54 | T_AVAL_REG | |
| 0x58 | T_IDLE_REG |
EXTCAP_HEADER register
- Absolute Address: 0x200
- Base Offset: 0x0
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | CAP_ID | r | 0xC1 | CAP_ID |
| 23:8 | CAP_LENGTH | r | 0x18 | CAP_LENGTH |
CAP_ID field
Extended Capability ID
CAP_LENGTH field
Capability Structure Length in DWORDs
SOC_MGMT_CONTROL register
- Absolute Address: 0x204
- Base Offset: 0x4
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | PLACEHOLDER | rw | 0x0 |
PLACEHOLDER field
SOC_MGMT_STATUS register
- Absolute Address: 0x208
- Base Offset: 0x8
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | PLACEHOLDER | rw | 0x0 |
PLACEHOLDER field
REC_INTF_CFG register
- Absolute Address: 0x20C
- Base Offset: 0xC
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | REC_INTF_BYPASS | rw | 0x0 | Recovery Interface access type |
| 1 | REC_PAYLOAD_DONE | rw | 0x0 | Recovery payload done |
REC_INTF_BYPASS field
Choose Recovery Interface access type:
-
0 - I3C Core
-
1 - direct AXI
REC_PAYLOAD_DONE field
Inform Recovery Handler that payload transfer is finished.
REC_INTF_REG_W1C_ACCESS register
- Absolute Address: 0x210
- Base Offset: 0x10
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | DEVICE_RESET_CTRL | rw | 0x0 | HW Device Reset Control |
| 15:8 | RECOVERY_CTRL_ACTIVATE_REC_IMG | rw | 0x0 | HW Activate Recovery Image |
| 23:16 | INDIRECT_FIFO_CTRL_RESET | rw | 0x0 | HW Indirect FIFO Reset Control |
DEVICE_RESET_CTRL field
Write to 'Reset control - Device Reset Control' register, mocking a hardware access (bypassing 'write 1 to clear' register property).
RECOVERY_CTRL_ACTIVATE_REC_IMG field
Write to 'Recovery Control - Activate Recovery Image' register, mocking a hardware access (bypassing 'write 1 to clear' register property).
INDIRECT_FIFO_CTRL_RESET field
Write to 'Indirect memory configuration - reset' register, mocking a hardware access (bypassing 'write 1 to clear' register property).
SOC_MGMT_RSVD_2 register
- Absolute Address: 0x214
- Base Offset: 0x14
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | PLACEHOLDER | rw | 0x0 |
PLACEHOLDER field
SOC_MGMT_RSVD_3 register
- Absolute Address: 0x218
- Base Offset: 0x18
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | PLACEHOLDER | rw | 0x0 |
PLACEHOLDER field
SOC_PAD_CONF register
- Absolute Address: 0x21C
- Base Offset: 0x1C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 0 | INPUT_ENABLE | rw | 0x1 | Enable Input |
| 1 | SCHMITT_EN | rw | 0x0 | Schmitt Trigger Enable |
| 2 | KEEPER_EN | rw | 0x0 | High-Keeper Enable |
| 3 | PULL_DIR | rw | 0x0 | Pull Direction |
| 4 | PULL_EN | rw | 0x0 | Pull Enable |
| 5 | IO_INVERSION | rw | 0x0 | IO INVERSION |
| 6 | OD_EN | rw | 0x0 | Open-Drain Enable |
| 7 | VIRTUAL_OD_EN | rw | 0x0 | Virtual Open Drain Enable |
| 31:24 | PAD_TYPE | rw | 0x1 | Pad type |
INPUT_ENABLE field
Enable input:
0 - enabled
1 - disabled
SCHMITT_EN field
Enable the Schmitt Trigger:
0 - disabled
1 - enabled
KEEPER_EN field
Enable the High-Keeper:
0 - disabled
1 - enabled
PULL_DIR field
Direction of the pull:
0 - Pull down
1 - Pull up
PULL_EN field
Enable Pull:
0 - disabled
1 - enabled
IO_INVERSION field
Invert I/O signal:
0 - signals pass-through
1 - signals are inverted
OD_EN field
Enable Open-Drain:
0 - disabled
1 - enabled
VIRTUAL_OD_EN field
Enable virtual open drain:
0 - disabled
1 - enabled
PAD_TYPE field
Select pad type
0 - Bidirectional
1 - Open-drain
2 - Input-only
3 - Analog input
SOC_PAD_ATTR register
- Absolute Address: 0x220
- Base Offset: 0x20
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 15:8 | DRIVE_SLEW_RATE | rw | 0xF | Driver Slew Rate |
| 31:24 | DRIVE_STRENGTH | rw | 0xF | Driver Strength |
DRIVE_SLEW_RATE field
Select driver slew rate
'0 - lowest
'1 - highest
DRIVE_STRENGTH field
Select driver strength
'0 - lowest
'1 - highest
SOC_MGMT_FEATURE_2 register
- Absolute Address: 0x224
- Base Offset: 0x24
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | PLACEHOLDER | rw | 0x0 |
PLACEHOLDER field
Reserved for: I/O ring and pad configuration
SOC_MGMT_FEATURE_3 register
- Absolute Address: 0x228
- Base Offset: 0x28
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | PLACEHOLDER | rw | 0x0 |
PLACEHOLDER field
Reserved for: I/O ring and pad configuration
T_R_REG register
- Absolute Address: 0x22C
- Base Offset: 0x2C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 19:0 | T_R | rw | 0x0 |
T_R field
Rise time of both SDA and SCL in clock units
T_F_REG register
- Absolute Address: 0x230
- Base Offset: 0x30
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 19:0 | T_F | rw | 0x0 |
T_F field
Fall time of both SDA and SCL in clock units
T_SU_DAT_REG register
- Absolute Address: 0x234
- Base Offset: 0x34
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 19:0 | T_SU_DAT | rw | 0x0 |
T_SU_DAT field
Data setup time in clock units
T_HD_DAT_REG register
- Absolute Address: 0x238
- Base Offset: 0x38
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 19:0 | T_HD_DAT | rw | 0x0 |
T_HD_DAT field
Data hold time in clock units
T_HIGH_REG register
- Absolute Address: 0x23C
- Base Offset: 0x3C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 19:0 | T_HIGH | rw | 0x0 | High period of the SCL in clock units |
T_HIGH field
T_LOW_REG register
- Absolute Address: 0x240
- Base Offset: 0x40
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 19:0 | T_LOW | rw | 0x0 |
T_LOW field
Low period of the SCL in clock units
T_HD_STA_REG register
- Absolute Address: 0x244
- Base Offset: 0x44
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 19:0 | T_HD_STA | rw | 0x0 |
T_HD_STA field
Hold time for (repeated) START in clock units
T_SU_STA_REG register
- Absolute Address: 0x248
- Base Offset: 0x48
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 19:0 | T_SU_STA | rw | 0x0 |
T_SU_STA field
Setup time for repeated START in clock units
T_SU_STO_REG register
- Absolute Address: 0x24C
- Base Offset: 0x4C
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 19:0 | T_SU_STO | rw | 0x0 |
T_SU_STO field
Setup time for STOP in clock units
T_FREE_REG register
- Absolute Address: 0x250
- Base Offset: 0x50
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | T_FREE | rw | 0xC |
T_FREE field
T_AVAL_REG register
- Absolute Address: 0x254
- Base Offset: 0x54
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | T_AVAL | rw | 0x12C |
T_AVAL field
T_IDLE_REG register
- Absolute Address: 0x258
- Base Offset: 0x58
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 31:0 | T_IDLE | rw | 0xEA60 |
T_IDLE field
CtrlCfg register file
- Absolute Address: 0x260
- Base Offset: 0x160
- Size: 0x8
| Offset | Identifier | Name |
|---|---|---|
| 0x0 | EXTCAP_HEADER | β |
| 0x4 | CONTROLLER_CONFIG | Controller Config |
EXTCAP_HEADER register
- Absolute Address: 0x260
- Base Offset: 0x0
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | CAP_ID | r | 0x2 | CAP_ID |
| 23:8 | CAP_LENGTH | r | 0x2 | CAP_LENGTH |
CAP_ID field
Extended Capability ID
CAP_LENGTH field
Capability Structure Length in DWORDs
CONTROLLER_CONFIG register
- Absolute Address: 0x264
- Base Offset: 0x4
- Size: 0x4
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 5:4 | OPERATION_MODE | r | 0x1 | Operation Mode |
OPERATION_MODE field
TERMINATION_EXTCAP_HEADER register
- Absolute Address: 0x268
- Base Offset: 0x168
- Size: 0x4
Register after the last EC must advertise ID == 0. Termination register is added to guarantee that the discovery mechanism reaches termination value.
| Bits | Identifier | Access | Reset | Name |
|---|---|---|---|---|
| 7:0 | CAP_ID | r | 0x0 | CAP_ID |
| 23:8 | CAP_LENGTH | r | 0x1 | CAP_LENGTH |
CAP_ID field
Extended Capability ID
CAP_LENGTH field
Capability Structure Length in DWORDs
9248d79
Caliptra - ROM Specification v2.1
Spec Version: 1.0
Scope
Caliptra is an open-source Hardware Root of Trust for Measurement (RTM). This document is the architecture specification for Caliptra Read Only Memory Code (ROM). As an architecture specification for ROM, this document describes the following topics:
-
Provide high level architecture
-
Describe ROM DICE Layering Architecture
-
Describe ROM functionality
-
Define ROM boot flows
- Cold Reset Flow
- Warm Reset Flow
- Update Reset Flow
- Unknown/Spurious Reset Flow
-
Cryptographic Derivations
Spec Opens
- Describe in-memory logs
Glossary
| Term | Description |
|---|---|
| CDI | Compound Device Identity |
| CSR | Certificate Signing Request |
| DCCM | Data Closely Coupled Memory |
| DICE | Device Identifier Composition Engine |
| ECC | Elliptic Curve Cryptography |
| FHT | Firmware Handoff Table |
| FMC | First Mutable Code |
| FW | Firmware |
| ICCM | Instruction Closely Coupled Memory |
| IDEVID | Initial Device ID DICE Layer |
| MLDSA | Module-Lattice-Based Digital Signature Algorithm |
| RoT | Root of Trust |
| RT | Runtime |
| RTM | Root of Trust for Measurement |
| TCI | Trusted Component Identifier |
| UDS | Unique Device Secret |
| SVN | Security Version Number |
| X509 | Digital Certificate Standard |
Fuse and architectural registers
Following are the main FUSE & Architectural Registers used by the Caliptra ROM for DICE Derivations:
Fuse Registers
| Register | Width (bits) | Description |
|---|---|---|
| FUSE_UDS_SEED | 512 | Obfuscated UDS |
| FUSE_FIELD_ENTROPY | 256 | Obfuscated Field Entropy |
| FUSE_VENDOR_PK_HASH | 384 | Hash of the ECC and LMS or MLDSA Manufacturer Public Key Descriptors |
| FUSE_ECC_REVOCATION | 4 | Manufacturer ECC Public Key Revocation Mask |
| FUSE_LMS_REVOCATION | 32 | Manufacturer LMS Public Key Revocation Mask |
| FUSE_MLDSA_REVOCATION | 4 | Manufacturer MLDSA Public Key Revocation Mask |
| FUSE_FIRMWARE_SVN | 128 | Firmware Security Version Number |
| FUSE_ANTI_ROLLBACK_DISABLE | 1 | Disable SVN checking for firmware when bit is set |
| FUSE_IDEVID_CERT_ATTR | 768 | FUSE containing information for generating IDEVID CSR Word 0:bits[0-2]: ECDSA X509 Key Id Algorithm (3 bits) 0: SHA1, 1: SHA256, 2: SHA384, 3: SHA512, 4: Fuse Word 0:bits[3-5]: MLDSA X509 Key Id Algorithm (3 bits) 0: SHA1, 1: SHA256, 2: SHA384, 3: SHA512, 4: Fuse Word 1,2,3,4,5: ECDSA Subject Key Id Word 6,7,8,9,10: MLDSA Subject Key Id Words 11: UEID type as defined in IETF RATS specification Words 12,13,14,15: Manufacturer Serial Number |
| FUSE_MANUF_DEBUG_UNLOCK_TOKEN | 512 | SHA-512 digest of secret value for manufacturing debug unlock authorization |
| FUSE_PQC_KEY_TYPE | 2 | One-hot encoded selection of PQC key type for firmware validation. Bit 0: MLDSA Bit 1: LMS |
Architectural Registers
| Register | Width (bits) | Description |
|---|---|---|
| CPTRA_OWNER_PK_HASH | 384 | Owner ECC and LMS or MLDSA Public Key Hash |
Entropy Source Configuration Registers
The ROM configures the entropy source (CSRNG) during initialization using the following registers:
| Register | Field/Bits | Description |
|---|---|---|
| SS_STRAP_GENERIC[2] | [15:0] | Health test window size for FIPS mode (default: 512). This is the window size for all health tests when entropy is tested in FIPS mode. |
| SS_STRAP_GENERIC[2] | [31] | Entropy bypass mode. When set to 1, enables bypass mode (es_type) to allow entropy characterization directly without passing through conditioning. |
| CPTRA_I_TRNG_ENTROPY_CONFIG_0 | [15:0] | Adaptive Proportion test high threshold (default: 1536). The test fails if any window has more than this threshold of 1's. |
| CPTRA_I_TRNG_ENTROPY_CONFIG_0 | [31:16] | Adaptive Proportion test low threshold (default: 512). The test fails if any window has less than this threshold of 1's. |
| CPTRA_I_TRNG_ENTROPY_CONFIG_1 | [15:0] | Repetition Count test threshold (default: 41). The test fails if an RNG wire repeats the same bit this many times in a row. |
| CPTRA_I_TRNG_ENTROPY_CONFIG_1 | [31:16] | Alert threshold (default: 2). Number of health check failures before an alert is triggered. |
Notes:
- If any threshold value is set to 0, the ROM uses the default value specified above.
- These configuration values are stored in persistent storage after first read to prevent malicious modification (reloaded on cold reset).
- In debug mode (
debug_locked == false), entropy source configuration registers remain unlocked for characterization. - In production mode, ROM locks the entropy source configuration after programming to prevent modification.
For a comprehensive overview of the SOC interface registers, please refer to the following link:: https://chipsalliance.github.io/caliptra-rtl/main/external-regs/?p=caliptra_top_reg.generic_and_fuse_reg
Firmware image bundle
The Caliptra Firmware image has two main components:
-
Firmware manifest
-
Firmware images
The firmware manifest is a combination of preamble and a signed header. It has public keys, public key hashes, signatures and table of contents which refer to the various firmware images contained in the bundle.
Firmware manifest
Firmware manifest consists of preamble, header and table of contents.
Preamble
It is the unsigned portion of the manifest. Preamble contains the signing public keys and signatures. ROM is responsible for parsing the preamble. ROM performs the following steps:
- Loads the preamble from the mailbox.
- Calculates the hash of ECC and LMS or MLDSA Public Key Descriptors in the preamble and compares it against the hash in the fuse (FUSE_VENDOR_PK_HASH). If the hashes do not match, the boot fails.
- Verifies the active Manufacturer Public Key(s) based on fuse (FUSE_ECC_REVOCATION for ECC public key, FUSE_LMS_REVOCATION for LMS public key or FUSE_MLDSA_REVOCATION for MLDSA public key)
Note: All fields are little endian unless specified
Preamble
| Field | Size (bytes) | Description |
|---|---|---|
| Firmware Manifest Marker | 4 | Magic Number marking the start of the package manifest. The value must be 0x434D4E32 (βCMN2β in ASCII) |
| Firmware Manifest Size | 4 | Size of the full manifest structure |
| Firmware Manifest Type | 4 | Byte0: - Type 0x1 β ECC & MLDSA Keys 0x3 β ECC & LMS Keys Byte1-Byte3: Reserved |
| Manufacturer ECC Key Descriptor | 196 | Public Key Descriptor for ECC keys |
| Manufacturer LMS or MLDSA Key Descriptor | 1540 | Public Key Descriptor for LMS (1540 bytes) or MLDSA (196 bytes + 1344 unused bytes) keys |
| Active ECC Key Index | 4 | Public Key Hash Index for the active ECC key |
| Active ECC Key | 96 | ECC P-384 public key used to verify the Firmware Manifest Header Signature X-Coordinate: Public Key X-Coordinate (48 bytes, big endian) Y-Coordinate: Public Key Y-Coordinate (48 bytes, big endian) |
| Active LMS or MLDSA Key Index | 4 | Public Key Hash Index for the active LMS or MLDSA key |
| Active LMS or MLDSA Key | 2592 | LMS public key (48 bytes + 2544 unused bytes) used to verify the Firmware Manifest Header Signature. tree_type: LMS Algorithm Type (4 bytes, big endian) Must equal 12. otstype: LM-OTS Algorithm Type (4 bytes, big endian) Must equal 7. id: (16 bytes) digest: (24 bytes) OR MLDSA-87 public key used to verify the Firmware Manifest Header Signature. (2592 bytes, little endian) |
| Manufacturer ECC Signature | 96 | Manufacturer ECC P-384 signature of the Firmware Manifest header hashed using SHA2-384. R-Coordinate: Random Point (48 bytes) S-Coordinate: Proof (48 bytes) |
| Manufacturer LMS or MLDSA Signature | 4628 | Manufacturer LMS signature (1620 bytes + 3008 unused bytes) of the Firmware Manifest header hashed using SHA2-384. q: Leaf of the Merkle tree where the OTS public key appears (4 bytes) ots: Lmots Signature (1252 bytes) tree_type: Lms Algorithm Type (4 bytes) tree_path: Path through the tree from the leaf associated with the LM-OTS signature to the root. (360 bytes) OR Vendor MLDSA-87 signature of the Firmware Manifest header hashed using SHA2-512 (4627 bytes + 1 Reserved byte, little endian) |
| Owner ECC Public Key | 96 | ECC P-384 public key used to verify the Firmware Manifest Header Signature. X-Coordinate: Public Key X-Coordinate (48 bytes) Y-Coordinate: Public Key Y-Coordinate (48 bytes) |
| Owner LMS or MLDSA Public Key | 2592 | LMS public key (48 bytes + 2544 unused bytes) used to verify the Firmware Manifest Header Signature. tree_type: LMS Algorithm Type (4 bytes) otstype: LMS Ots Algorithm Type (4 bytes) id: (16 bytes) digest: (24 bytes) OR MLDSA-87 public key used to verify the Firmware Manifest Header Signature. (2592 bytes, little endian) |
| Owner ECC Signature | 96 | Manufacturer ECC P-384 signature of the Firmware Manifest header hashed using SHA2-384. R-Coordinate: Random Point (48 bytes) S-Coordinate: Proof (48 bytes) |
| Owner LMS or MLDSA Signature | 4628 | Owner LMS signature (1620 bytes + 3008 unused bytes) of the Firmware Manifest header hashed using SHA2-384. q: Leaf of the Merkle tree where the OTS public key appears (4 bytes) ots: Lmots Signature (1252 bytes) tree_type: Lms Algorithm Type (4 bytes) tree_path: Path through the tree from the leaf associated with the LM-OTS signature to the root. (360 bytes) OR Owner MLDSA-87 signature of the Firmware Manifest header hashed using SHA2-512 (4627 bytes + 1 Reserved byte, little endian) |
| Reserved | 8 | Reserved 8 bytes |
ECC Manufacturer Public Key Descriptor
| Field | Size (bytes) | Description |
|---|---|---|
| Key Descriptor Version | 2 | Version of the Key Descriptor. The value must be 0x1 for Caliptra 2.x |
| Reserved | 1 | Reserved |
| Key Hash Count | 1 | Number of valid public key hashes |
| Public Key Hash(es) | 48 * n | List of valid and invalid (if any) SHA2-384 public key hashes. ECDSA: n = 4 |
PQC Manufacturer Public Key Descriptor
| Field | Size (bytes) | Description |
|---|---|---|
| Key Descriptor Version | 2 | Version of the Key Descriptor. The value must be 0x1 for Caliptra 2.x |
| Key Type | 1 | Type of the key in the descriptor 0x1 - MLDSA 0x3 - LMS |
| Key Hash Count | 1 | Number of valid public key hashes |
| Public Key Hash(es) | 48 * n | List of valid and invalid (if any) SHA2-384 public key hashes. LMS: n = 32, MLDSA: n = 4 |
Header
The header contains the security version and SHA2-384 hash of the table of contents. Header is the only signed component in the image. Signing the header is enough as the table of contents contains the hashes of the individual firmware images. This technique reduces the number of signature verifications required to be performed during boot.
| Field | Size (bytes) | Description |
|---|---|---|
| Revision | 8 | 8-byte version of the firmware image bundle |
| Vendor ECC public key hash index | 4 | The hint to ROM to indicate which ECC public key hash it should use to validate the active ECC public key. |
| Vendor LMS or MLDSA public key hash index | 4 | The hint to ROM to indicate which LMS or MLDSA public key hash it should use to validate the active public key. |
| Flags | 4 | Feature flags. Bit0: - Interpret the pl0_pauser field. If not set, all PAUSERs are PL1 Bit1-Bit31: Reserved |
| TOC Entry Count | 4 | Number of entries in TOC. |
| PL0 PAUSER | 4 | The PAUSER with PL0 privileges. |
| TOC Digest | 48 | SHA2-384 Digest of table of contents. |
| Vendor Data | 40 | Vendor Data. Not Before: Vendor Start Date [ASN1 Time Format] For Alias FMC and Alias RT certificates (15 bytes) Not After: Vendor End Date [ASN1 Time Format] For Alias FMC and Alias RT certificates (15 bytes) Reserved: (10 bytes) |
| Owner Data | 40 | Owner Data. Not Before: Owner Start Date [ASN1 Time Format] For Alias FMC and Alias RT certificates. Takes preference over vendor start date (15 bytes) Not After: Owner End Date [ASN1 Time Format] For Alias FMC and Alias RT certificates. Takes preference over vendor end date (15 bytes) Reserved: (10 bytes) |
Table of contents
It contains the image information and SHA-384 hash of individual firmware images.
| Field | Size (bytes) | Description |
|---|---|---|
| TOC Entry Id | 4 | TOC Entry Id. The fields can have following values: 0x0000_0001: FMC 0x0000_0002: Runtime |
| Image Type | 4 | Image Type that defines format of the image section 0x0000_0001: Executable |
| Image Revision | 20 | Git Commit hash of the build |
| Image Version | 4 | Firmware release number |
| Image SVN | 4 | Security Version Number for the image. It is compared to FW SVN fuses. FMC TOC entry's SVN field is ignored. |
| Reserved | 4 | Reserved field |
| Image Load Address | 4 | Load address |
| Image Entry Point | 4 | Entry point to start the execution from |
| Image Offset | 4 | Offset from beginning of the image |
| Image Size | 4 | Image Size |
| Image Hash | 48 | SHA2-384 hash of image |
Image
| Field | Size (bytes) | Description |
|---|---|---|
| Data | N | Image content |
Cryptographic primitives
The following sections define the various cryptographic primitives used by Caliptra ROM:
| Group | Operation | Description |
|---|---|---|
| Deobfuscation Engine | doe_decrypt_uds(kv_slot, iv) | Decrypt UDS to the specified key vault slot with specified initialization vector Input: kv_slot - key vault slot to decrypt the uds to iv - initialization vector |
doe_decrypt_fe(kv_slot, iv) | Decrypt Field Entropy to the specified key vault slot with specified initialization vector Input: kv_slot - key vault slot to decrypt the field entropy to iv - initialization vector | |
doe_clear_secrets() | Clear UDS Fuse Register, Field Entropy Fuse Register and Obfuscation key | |
| Hashed Message Authentication Code | hmac_mac(key,data,mac_kv_slot,mode) | Calculate the MAC using a caller provided key and data. The resultant MAC is stored in key vault slot Input: key - caller specified key data - data mac_kv_slot - key vault slot to store the MAC to mode - HMAC384 or HMAC512 |
hmac_mac(kv_slot,data,mac_kv_slot) | Calculate the MAC using a caller provided key and data. The resultant MAC is stored in key vault slot Input: kv_slot - key vault slot to use the key from data - data mac_kv_slot - key vault slot to store the MAC to mode - HMAC384 or HMAC512 | |
hmac512_mac(key,data,mac_kv_slot) | Calculate the MAC using a caller provided key and data. The resultant MAC is stored in key vault slot Input: key - caller specified key data - data mac_kv_slot - key vault slot to store the MAC to | |
hmac512_mac(kv_slot,data,mac_kv_slot) | Calculate the MAC using a caller provided key and data. The resultant MAC is stored in key vault slot Input: kv_slot - key vault slot to use the key from data - data mac_kv_slot - key vault slot to store the MAC to | |
| Elliptic Curve Cryptography | ecc384_keygen(seed_kv_slot, priv_kv_slot) -> pub_key | Generate ECC384 Key Pair. Input: seed_key_slot - key vault slot to use as seed for key generation priv_kv_slot - key vault slot to store the private key to Output: pub-key - public key associated with the private key |
ecc384_sign(priv_kv_slot, data) -> sig | ECC384 signing operation Input: priv_kv_slot - key vault slot to use a private key from data - data to sign Output: sig - signature | |
ecc384_verify(pub_key, data, sig) -> CaliptraResult<Array4xN<12, 48>> | ECC384 verify operation Input: pub-key -public key data - data to verify sig - signature Output: Ecc384Result - verify.r value on success, else an error | |
| Module-Lattice-Based Digital Signature Algorithm | mldsa87_keygen(seed_kv_slot) -> pub_key | Generate Mldsa87 Key Pair. Input: seed_key_slot - key vault slot to use as seed for key generation Output: pub-key - public key associated with the private key |
mldsa87_sign(seed_kv_slot, data) -> sig | Mldsa87 signing operation Input: seed_kv_slot - key vault slot to use as seed for key generation for signing data - data to sign Output: sig - signature | |
mldsa87_verify(pub_key, data, sig) -> Mldsa87Result | Mldsa87 verify operation Input: pub-key -public key data - data to verify sig - signature Output: Mldsa87Result - '0xAAAAAAAA' value on success, '0x55555555' on error | |
| Secure Hash Algorithm | sha384_digest(data) -> digest | Calculate the digest of the data Input: data - data to verify Output: digest - digest of the data |
| Key Vault | kv_clear(kv_slot) | Key Vault slot to clear Input: kv_slot - key vault slot to clear |
| Data Vault | dv48_store(data, dv_slot) | Store the 48-byte data in the specified data vault slot Input: data - data to store dv_slot - data vault slot |
dv48_lock_wr(dv_slot) | Write Lock the 48-byte data vault slot Input dv_slot - data vault slot | |
dv4_store(data, dv_slot) | Store the 4- byte data in the specified data vault slot Input data - data to store dv_slot - data vault slot | |
dv4_lock_wr(dv_slot) | Write Lock the 4-byte data vault slot Input dv_slot - data vault slot | |
| Platform Configuration Registers | pcr_extend(pcr_slot, data) | Perform PCR extend operation on a PCR with specified data Input: pcr_slot - PCR slot to hash extend data β data |
pcr_read(pcr_slot) -> measurement | Read the PCR slot Input: pcr_slot - PCR slot to read Output: measurement - Accumulated measurement | |
pcr_lock_clear(pcr_slot) | Lock for Clear PCR slot Input: pcr_slot - pcr slot | |
pcr_clear(pcr_slot) | Clear PCR slot Input: pcr_slot - pcr slot | |
| X509 | gen_tbs(type, pub_key) -> tbs | Generate X509 Certificate or CSR To Be Signed portionInput: type - Can be IDEVID_CSR, LDEVID_CERT or ALIAS_FMC_CERT pub-key -public key Output: tbs - DER encoded To Be Signed portion |
Well known cryptographic constants
| Constant | Size (bytes) | Description |
|---|---|---|
| DOE_IV | 16 | Initialization vector specified by the ROM for deobfuscating the UDS and Field Entropy. |
Cold reset flow
ROM performs all the necessary crypto derivations on cold reset. No crypto derivations are performed during warm reset or update reset.
Note that KvSlot3 is generally used as a temporary location for derived keying material during ECC keygen.
Initialization
The initialization step involves a traditional startup script for microcontroller. The initialization script performs following:
- Resets instruction counter
- Disables interrupts
- Clears all general purpose registers
- Sets up memory region attributes (Cacheable & Side effects)
- Sets up stack pointer
- Sets up NMI and Exception handler
- Zeros ICCM & DCCM memories (to initialize ECC)
- Jumps to Rust entry point
The following flows are conducted exclusively when the ROM is operating in SUBSYSTEM mode.
Manufacturing Flows:
The following flows are conducted when the ROM is operating in the manufacturing mode, indicated by a value of DEVICE_MANUFACTURING (0x1) in the CPTRA_SECURITY_STATE register device_lifecycle bits.
UDS Provisioning
-
On reset, the ROM checks if the
UDS_PROGRAM_REQbit in theSS_DBG_MANUF_SERVICE_REG_REQregister is set. If the bit is set, ROM initiates the UDS seed programming flow by setting theUDS_PROGRAM_IN_PROGRESSbit in theSS_DBG_MANUF_SERVICE_REG_RSPregister. If the flow fails at some point past reading the REQ bits, the flow will be aborted and an error returned. -
ROM then retrieves the following values:
- A 512-bit value from the iTRNG.
- The UDS Seed programming base address from the
SS_UDS_SEED_BASE_ADDR_LandSS_UDS_SEED_BASE_ADDR_Hregisters. - The Fuse Controller's base address from the
SS_OTP_FC_BASE_ADDR_LandSS_OTP_FC_BASE_ADDR_Hregisters.
-
ROM then retrieves the UDS granularity from the
CPTRA_GENERIC_INPUT_WIRESregister0 Bit31 to learn if the fuse row is accessible with 32-bit or 64-bit granularity. If the bit is reset, it indicates 64-bit granularity; otherwise, it indicates 32-bit granularity. -
ROM computes the following values:
- DAI_IDLE bit offset: (
SS_STRAP_GENERICregister0 >> 16) & 0xFFFF DIRECT_ACCESS_CMDoffset: (SS_STRAP_GENERICregister1) & 0xFFFF + Fuse Controller's base address.
- DAI_IDLE bit offset: (
-
ROM then performs the following steps until all the 512 bits of the UDS seed are programmed:
- The ROM verifies the idle state of the DAI by reading the
STATUSregisterDAI_IDLEbit (offset retrieved above) of the Fuse Controller, located at offset 0x10 from the Fuse Controller's base address. - If the granularity is 32-bit, the ROM writes the next word from the UDS seed to the
DIRECT_ACCESS_WDATA_0register. If the granularity is 64-bit, the ROM writes the next two words tothe DIRECT_ACCESS_WDATA_0andDIRECT_ACCESS_WDATA_1registers, located at offsets 0x8 and 0xC respectively from theDIRECT_ACCESS_CMDregister. - The ROM writes the lower 32 bits of the UDS Seed programming base address to the
DIRECT_ACCESS_ADDRESSregister, located at offset 0x4 from theDIRECT_ACCESS_CMDregister. - The ROM triggers the UDS seed write command by writing 0x2 to the
DIRECT_ACCESS_CMDregister.. - The ROM increments the
DIRECT_ACCESS_ADDRESSregister by 4 for 32-bit granularity or 8 for 64-bit granularity and repeats the process for the remaining words of the UDS seed.
- The ROM verifies the idle state of the DAI by reading the
-
The ROM continuously polls the Fuse Controller's
STATUSregister until the DAI state returns to idle. -
After completing the write operation, ROM triggers the partition digest operation performing the following steps:
- The ROM writes the lower 32 bits of the UDS Seed programming base address to the
DIRECT_ACCESS_ADDRESSregister. - The ROM triggers the digest calculation command by writing 0x4 to the
DIRECT_ACCESS_CMDregister. - The ROM continuously polls the Fuse Controller's
STATUSregister until the DAI state returns to idle.
- The ROM writes the lower 32 bits of the UDS Seed programming base address to the
-
ROM updates the
UDS_PROGRAM_SUCCESSor theUDS_PROGRAM_FAILbit in theSS_DBG_MANUF_SERVICE_REG_RSPregister to indicate the outcome of the operation. -
ROM then resets the
UDS_PROGRAM_IN_PROGRESSbit in theSS_DBG_MANUF_SERVICE_REG_RSPregister to indicate completion of the programming. -
The manufacturing process then polls this bit and continues with the fuse burning flow as outlined by the fuse controller specifications and SOC-specific VR methodologies.
Debug Unlock
Note: All mailbox command requests start with a 4-byte MailboxReqHeader (containing a checksum field), while response payloads start with an 8-byte MailboxRespHeader (containing checksum and FIPS status fields).
-
On reset, the ROM checks if the
MANUF_DBG_UNLOCK_REQbit in theSS_DBG_MANUF_SERVICE_REG_REQregister and theDEBUG_INTENTbit inSS_DEBUG_INTENTregister are set. -
If they are set, the ROM sets the
TAP_MAILBOX_AVAILABLE&MANUF_DBG_UNLOCK_IN_PROGRESSbits in theSS_DBG_MANUF_SERVICE_REG_RSPregister, then enters a loop, awaiting aTOKENcommand on the mailbox. The payload of this command is a 256-bit value. -
The ROM performs a SHA-512 operation on the token to generate the input token digest.
-
The ROM compares the
FUSE_MANUF_DBG_UNLOCK_TOKENfuse register with the input token digest. -
The ROM completes the mailbox command.
-
If the input token digest and fuse token digests match, the ROM authorizes the debug unlock by setting the
SS_DBG_MANUF_SERVICE_REG_RSPregisterMANUF_DBG_UNLOCK_SUCCESSbit to 1. -
If the token digests do not match, the ROM blocks the debug unlock by setting the the
SS_DBG_MANUF_SERVICE_REG_RSPregisterMANUF_DBG_UNLOCK_FAILbit to 1. -
The ROM sets the
SS_DBG_MANUF_SERVICE_REG_RSPregisterMANUF_DBG_UNLOCK_IN_PROGRESSandTAP_MAILBOX_AVAILABLEbits to 0.
Production Flows
The following flows are conducted when the ROM is operating in the production mode, indicated by a value of DEVICE_PRODUCTION (0x3) in the CPTRA_SECURITY_STATE register device_lifecycle bits.
Debug Unlock
Note: All mailbox command requests start with a 4-byte MailboxReqHeader (containing a checksum field), while response payloads start with an 8-byte MailboxRespHeader (containing checksum and FIPS status fields).
-
On reset, the ROM checks if the
PROD_DEBUG_UNLOCK_REQbit in theSS_DBG_MANUF_SERVICE_REG_REQregister and theDEBUG_INTENTinSS_DEBUG_INTENTregister are set. -
If they are set, the ROM sets the
TAP_MAILBOX_AVAILABLE&PROD_DBG_UNLOCK_IN_PROGRESSbits in theSS_DBG_MANUF_SERVICE_REG_RSPregister. -
ROM enters a polling loop, awaiting a
AUTH_DEBUG_UNLOCK_REQcommand (Id: 0x50445552) on the mailbox. The payload for this command is of the following format:
| Field | Size (bytes) | Description |
|---|---|---|
| Length | 4 | Length of the message in DWORDs. This should be 2. |
| Unlock Level | 1 | Debug unlock Level (Number 1-8). |
| Reserved | 3 | Reserved field. |
-
On failure, ROM does the following:
PROD_DBG_UNLOCK_FAILbit inSS_DBG_MANUF_SERVICE_REG_RSPregister to 1.PROD_DBG_UNLOCK_IN_PROGRESSbit inSS_DBG_MANUF_SERVICE_REG_RSPregister to 0.
-
The ROM validates the payload and on successful validation sends the following payload as the response:
| Field | Size (bytes) | Description |
|---|---|---|
| Length | 4 | Length of the message in DWORDs. This should be 21. |
| Unique Device Identifier | 32 | Device identifier of the Caliptra Device. |
| Challenge | 48 | Random number. |
-
On failure, ROM sets the registers outlined in step 3.
-
The SOC then sends the following payload via the
AUTH_DEBUG_UNLOCK_TOKENmailbox command (0x50445554):
| Field | Size (bytes) | Description |
|---|---|---|
| Length | 4 | Length of the message in DWORDs. This should be 0x753. |
| Unique Device Identifier | 32 | Device identifier sent in AUTH_DEBUG_UNLOCK_CHALLENGE mailbox command payload. |
| Unlock Level | 1 | Debug unlock Level (Number 1-8). |
| Reserved | 3 | Reserved field. |
| Challenge | 48 | Random number sent in AUTH_DEBUG_UNLOCK_CHALLENGE mailbox command payload. |
| ECC Public Key | 96 | ECC P-384 public key used to verify the Message Signature X-Coordinate: Public Key X-Coordinate (48 bytes, big endian) Y-Coordinate: Public Key Y-Coordinate (48 bytes, big endian) |
| MLDSA Public Key | 2592 | MLDSA-87 public key used to verify the Message Signature. |
| ECC Signature | 96 | ECC P-384 signature of the Message hashed using SHA2-384. R-Coordinate: Random Point (48 bytes) S-Coordinate: Proof (48 bytes). |
| MLDSA Signature | 4628 | MLDSA signature of the Message hashed using SHA2-512. (4627 bytes + 1 Reserved byte). |
-
On receiving this payload, ROM performs the following validations:
- Ensures the value in the
Lengthfield matches the size of the payload. - Confirms that the
Debug unlock levelis not zero and does not exceed the value specified in theSS_NUM_OF_PROD_DEBUG_UNLOCK_AUTH_PK_HASHESregister. - Calculates the address of the public key hash fuse as follows:
SS_PROD_DEBUG_UNLOCK_AUTH_PK_HASH_REG_BANK_OFFSET register value + ( (Debug Unlock Level - 1) * SHA2-384 hash size (48 bytes) ) - Retrieves the SHA2-384 hash (48 bytes) from the calculated address using DMA assist.
- Computes the SHA2-384 hash of the message formed by concatenating the ECC and MLDSA public keys in the payload.
- Compares the retrieved and computed hashes. It the comparison fails, the ROM blocks the debug unlock request by setting the registers outlined in step 3.
- Upon hash comparison failure, the ROM exits the payload validation flow and completes the mailbox command.
- Ensures the value in the
-
The ROM proceeds with payload validation by verifying the ECC and MLDSA signatures over the
Challenge,Device Identifier,ReservedandUnlock Categoryfields within the payload. Should the validation fail, the ROM blocks the debug unlock by setting the registers outlined in item 3. Conversely, if the signature validation succeeds, the ROM authorizes the debug unlock by configuring the following settings:PROD_DBG_UNLOCK_SUCCESSbit inSS_DBG_MANUF_SERVICE_REG_RSPregister to 1.- Setting the Debug unlock level in the
SS_SOC_DBG_UNLOCK_LEVELregister as one hot encoded value (1 << (Level - 1)) PROD_DBG_UNLOCK_IN_PROGRESSbit inSS_DBG_MANUF_SERVICE_REG_RSPregister to 0.
-
ROM then completes the mailbox command with success.
-
The ROM sets the
SS_DBG_MANUF_SERVICE_REG_RSPregisterPROD_DBG_UNLOCK_IN_PROGRESSandTAP_MAILBOX_AVAILABLEbits to 0.
Known Answer Test (KAT)
To certify a cryptographic module, pre-operational self-tests must be performed when the system is booted. Implementing KATs is required for FIPS certification. However, regardless of FIPS certification, it is considered a security best practice to ensure that the supported cryptographic algorithms are functioning properly to guarantee correct security posture.
KAT execution is described as two types:
- Pre-operational Self-Test (POST)
- Conditional Algorithm Self-Test (CAST)
ROM performs the following POST tests to ensure that needed cryptographic modules are functioning correctly and are operational before any cryptographic operations are performed:
- SHA1
- SHA2-256
- SHA2-384
- SHA2-512
- SHA2-512-ACC
- ECC-384
- ECDH
- HMAC-384Kdf
- HMAC-512Kdf
- HKDF-384
- HKDF-512
- KDF-CMAC
- LMS
- MLDSA-87
- AES-256-ECB
- AES-256-CBC
- AES-256-CMAC
- AES-256-CTR
- AES-256-GCM
DICE Flow
Decrypt secrets
DICE Unique Device Secret (UDS) is stored in an SOC backed fuse (or derived from PUF). The raw UDS is not directly used. UDS is deobfuscated using Deobfuscation Engine. UDS is provisioned by the Silicon Vendor.
Field Entropy is used to mitigate certain classes of supply chain attacks. Field Entropy is programmed by the owner of the device in a secure environment in the ownerβs facility. Field Entropy programmed in fuses is not directly used. Field Entropy is put through the deobfuscation engine to randomize it.
Both UDS and Field Entropy are available only during cold reset of Caliptra.
Pre-conditions:
- Caliptra subsystem is being cold reset
- Obfuscation Key loaded in deobfuscation engine
- UDS and Field Entropy loaded in Caliptra Fuse Registers
- Keys Slot 0 - 23 are empty and Usage Bits are all cleared
- PCR 0 - 31 are all cleared
- DCCM datavault is cleared
Actions:
-
Decrypt UDS to Key Vault Slot 0
doe_decrypt_uds(KvSlot0, DOE_IV) -
Decrypt Field Entropy to Key Vault Slot 1
doe_decrypt_fe(KvSlot1, DOE_IV) -
Clear class secrets (Clears UDS, Field Entropy and Obfuscation Key cleared)
doe_clear_secrets()
Post-conditions:
- UDS Fuse Register and Field Entropy Fuse register cleared
- Obfuscation Key cleared from Deobfuscation Engine
- Vault State is as follows:
| Slot | Key Vault |
|---|---|
| 0 | UDS (64 bytes) |
| 1 | Field Entropy (32 bytes) |
Initial Device ID DICE layer
Initial Device ID Layer is used to generate Manufacturer CDI & Private Keys. This layer represents the manufacturer or silicon vendor DICE Identity. During manufacturing, ROM can be requested to create Certificate Signing Request (CSR) via JTAG.
Pre-conditions:
- UDS is in Key Vault Slot 0
Actions:
-
Derive the CDI using ROM specified label and UDS in Key Vault Slot 0 as data and store the resultant MAC in Key Vault Slot 6.
hmac512_kdf(KvSlot0, b"idevid_cdi", KvSlot6) -
Clear the UDS in the key vault.
kv_clear(KvSlot0) -
Derive ECC Key Pair using CDI in Key Vault Slot 6 and store the generated private key in Key Vault Slot 7.
IDevIDSeedEcdsa = hmac512_kdf(KvSlot6, b"idevid_ecc_key", KvSlot3)IDevIdPubKeyEcdsa = ecc384_keygen(KvSlot3, KvSlot7)kv_clear(KvSlot3)Derive the MLDSA Key Pair using CDI in Key Vault Slot 6 and store the key generation seed in Key Vault Slot 8.
IDevIDSeedMldsa = hmac512_kdf(KvSlot6, b"idevid_mldsa_key", KvSlot8)IDevIdPubKeyMldsa = mldsa87_keygen(KvSlot8)
(Note: Steps 4-12 are performed if CSR download is requested via CPTRA_DBG_MANUF_SERVICE_REG register)
-
Generate the
To Be SignedDER Blob of the IDevId CSR with the ECDSA public key.IDevIdTbsEcdsa = gen_tbs(IDEVID_CSR, IDevIdPubKeyEcdsa) -
Sign the IDevID
To Be SignedDER Blob with IDevId ECDSA Private Key in Key Vault Slot 7.IDevIdTbsDigestEcdsa = sha384_digest(IDevIdTbsEcdsa)IDevIdCertSigEcdsa = ecc384_sign(KvSlot7, IDevIdTbsDigestEcdsa) -
Verify the signature of IDevID
To Be SignedBlob.Result = ecc384_verify(IDevIdPubKeyEcdsa, IDevIdTbsDigestEcdsa, IDevIdCertSigEcdsa) -
Generate the
To Be SignedDER Blob of the IDevId CSR with the MLDSA public key.IDevIdTbsMldsa = gen_tbs(IDEVID_CSR, IDevIdPubKeyMldsa) -
Sign the IDevID
To Be SignedDER Blob with IDevId MLDSA Private Key generated from the seed in Key Vault Slot 8.IDevIdTbsDigestMldsa = sha512_digest(IDevIdTbsMldsa)IDevIdCertSigMldsa = mldsa87_sign(KvSlot8, IDevIdTbsDigestMldsa) -
Verify the signature of IDevID
To Be SignedBlob.Result = mldsa87_verify(IDevIdPubKeyMldsa, IDevIdTbsDigestMldsa, IDevIdCertSigMldsa) -
Generate the MACs over the tbs digests as follows:
IDevIdTbsEcdsaMac = hmac_mac(VendorSecretKvSlot, b"idevid_ecc_csr", IDevIdTbsDigestEcdsa, HmacMode::Hmac384)IDevIdTbsMldsaMac = hmac512_mac(VendorSecretKvSlot, b"idevid_mldsa_csr",IDevIdTbsDigestMldsa, HmacMode::Hmac512) -
Upload the CSR(s) to mailbox and wait for JTAG to read the CSR out of the mailbox. Format of the CSR payload is documented below:
IDevID CSR Format
Note: All fields are little endian unless specified
| Field | Size (bytes) | Description |
|---|---|---|
| Marker | 4 | Magic Number marking the start of the CSR payload. The value must be 0x435352 (βCSRβ in ASCII). |
| Size | 4 | Size of the entire CSR payload. |
| ECC CSR Size | 4 | Size of the ECC CSR in bytes. |
| ECC CSR | 512 | ECC CSR buffer. Actual CSR size is indicated by 'ECC CSR Size'. |
| MLDSA CSR Size | 4 | Size of the MLDSA CSR in bytes. |
| MLDSA CSR | 7680 | MLDSA CSR bytes. Actual CSR size is indicated by 'MLDSA CSR Size'. |
| CSR MAC | 64 | HMAC-512 MAC, computed over the envelope bytes up to but not including this field. |
Post-conditions:
- Vault state is as follows:
| Slot | Key Vault |
|---|---|
| 1 | Field Entropy (32 bytes) |
| 6 | IDevID CDI (64 bytes) |
| 7 | IDevID ECDSA Private Key (48 bytes) |
| 8 | IDevID MLDSA Key Pair Seed (32 bytes) |
| DCCM Datavault |
|---|
| πIDevID Cert ECDSA Signature |
| πIDevID ECDSA Pub Key |
| πIDevID Cert MLDSA Signature |
| πIDevID MLDSA Pub Key |
Local Device ID DICE layer
Local Device ID Layer derives the Owner CDI, ECC and MLDSA Keys. This layer represents the owner DICE Identity as it is mixed with the Field Entropy programmed by the Owner.
Pre-conditions:
- Field Entropy is loaded in Key Vault Slot 1
- IDevID CDI is stored in Key Vault Slot 6
- IDevID Private Key is stored in Key Vault Slot 7
- IDevID MLDSA Key Generation Seed is stored in Key Vault Slot 8
Actions:
-
Derive the stable identity root secret from IDevID and store the resultant MAC in Key Vault Slot 0.
hmac512_mac(KvSlot0, b"stable_identity_root_idev", KvSlot6) -
Derive the LDevID CDI using IDevID CDI in Key Vault Slot 6 as HMAC Key and Field Entropy stored in Key Vault Slot 1 as data. The resultant MAC is stored back in Key Vault Slot 6.
hmac512_mac(KvSlot6, b"ldevid_cdi", KvSlot6)hmac512_mac(KvSlot6, KvSlot1, KvSlot6)
(Note: this uses a pair of HMACs to incorporate the diversification label, rather than a single KDF invocation, due to hardware limitations when passing KV data to the HMAC hardware as a message.)
-
Clear the Field Entropy in Key Vault Slot 1.
kv_clear(KvSlot1) -
Derive the stable identity root secret from LDevID and store the resultant MAC in Key Vault Slot 1.
hmac512_mac(KvSlot1, b"stable_identity_root_ldev", KvSlot6) -
Derive ECDSA Key Pair using CDI in Key Vault Slot 6 and store the generated private key in Key Vault Slot 5.
LDevIDSeed = hmac512_kdf(KvSlot6, b"ldevid_ecc_key", KvSlot3)LDevIdPubKeyEcdsa = ecc384_keygen(KvSlot3, KvSlot5)kv_clear(KvSlot3) -
Derive the MLDSA Key Pair using CDI in Key Vault Slot 6 and store the key generation seed in Key Vault Slot 4.
LDevIDSeed = hmac512_kdf(KvSlot6, b"ldevid_mldsa_key", KvSlot4)LDevIdPubKeyMldsa = mldsa87_keygen(KvSlot4) -
Store and lock (for write) the LDevID ECDSA and MLDSA Public Keys in the DCCM datavault.
-
Generate the
To Be SignedDER Blob of the ECDSA LDevId Certificate.LDevIdTbsEcdsa = gen_cert_tbs(LDEVID_CERT, IDevIdPubKeyEcdsa, LDevIdPubKeyEcdsa) -
Sign the LDevID
To Be SignedDER Blob with IDevId ECDSA Private Key in Key Vault Slot 7.LDevIdTbsDigestEcdsa = sha384_digest(LDevIdTbsEcdsa)LDevIdCertSigEcdsa = ecc384_sign(KvSlot7, LDevIdTbsDigestEcdsa) -
Clear the IDevId ECDSA Private Key in Key Vault Slot 7.
kv_clear(KvSlot7) -
Verify the signature of LDevID
To Be SignedBlob.Result = ecc384_verify(IDevIdPubKeyEcdsa, LDevIdTbsDigestEcdsa, LDevIdCertSigEcdsa) -
Generate the
To Be SignedDER Blob of the MLDSA LDevId Certificate.LDevIdTbsMldsa = gen_cert_tbs(LDEVID_CERT, IDevIdPubKeyMldsa, LDevIdPubKeyMldsa) -
Sign the LDevID
To Be SignedDER Blob with the IDevId MLDSA Private Key derived from the seed in Key Vault Slot 8.LDevIdTbsDigestMldsa = sha512_digest(LDevIdTbsMldsa)LDevIdCertSigMldsa = mldsa87_sign(KvSlot8, LDevIdTbsDigestMldsa) -
Clear the IDevId Mldsa seed in Key Vault Slot 8.
kv_clear(KvSlot8) -
Verify the signature of LDevID
To Be SignedBlob.Result = mldsa87_verify(IDevIdPubKeyMldsa, LDevIdTbsDigestMldsa, LDevIdCertSigMldsa) -
Store and lock (for write) the LDevID Certificate ECDSA and MLDSA Signatures in the DCCM datavault.
Post-conditions:
- Vault state is as follows:
| Slot | Key Vault |
|---|---|
| 0 | Stable identity root IDevID secret (64 bytes) |
| 1 | Stable identity root LDevID secret (64 bytes) |
| 4 | LDevID Key Pair Seed - MLDSA (32 bytes) |
| 5 | LDevID Private Key - ECDSA (48 bytes) |
| 6 | LDevID CDI (64 bytes) |
| DCCM Datavault |
|---|
| πIDevID Cert ECDSA Signature |
| πIDevID ECDSA Pub Key |
| πIDevID Cert MLDSA Signature |
| πIDevID MLDSA Pub Key |
| πLDevID Cert ECDSA Signature |
| πLDevID ECDSA Pub Key |
| πLDevID Cert MLDSA Signature |
| πLDevID MLDSA Pub Key |
Firmware Processor Stage
During this phase, the ROM executes specific mailbox commands. Based on the operational mode (SUBSYSTEM versus PASSIVE), the ROM also initiates the download of the firmware image. This download is conducted either through a mailbox command or via the Recovery Register Interface.
Handling commands from mailbox
ROM supports the following set of commands before handling the FW_DOWNLOAD command in PASSIVE mode (described in section 9.6) or RI_DOWNLOAD_FIRMWARE/RI_DOWNLOAD_ENCRYPTED_FIRMWARE command in SUBSYSTEM mode. Once the FW_DOWNLOAD, RI_DOWNLOAD_FIRMWARE, or RI_DOWNLOAD_ENCRYPTED_FIRMWARE is issued, ROM stops processing any additional mailbox commands.
-
STASH_MEASUREMENT: Up to eight measurements can be sent to the ROM for recording. Sending more than eight measurements will result in an FW_PROC_MAILBOX_STASH_MEASUREMENT_MAX_LIMIT fatal error. Format of a measurement is documented at Stash Measurement command.
-
VERSION: Get version info about the module. Version command.
-
SELF_TEST_START: This command is used to invoke the FIPS Known-Answer-Tests (aka KAT) on demand. Self Test Start command.
-
SELF_TEST_GET_RESULTS: This command is used to check if a SELF_TEST command is in progress. Self Test Get Results command.
-
SHUTDOWN: This command is used clear the hardware crypto blocks including the keyvault. Shutdown command.
-
CAPABILITIES: This command is used to query the ROM capabilities. Capabilities is a 128-bit value with individual bits indicating a specific capability. Capabilities are documented in the Capabilities command.
-
GET_IDEVID_CSR: This command is used to fetch the IDevID CSR from ROM. Fetch IDevIDCSR command.
-
CM_DERIVE_STABLE_KEY: This command is used to derive a stable key for Device Ownership Transfer or other flows. CM_DERIVE_STABLE_KEY
-
CM_HMAC: This command uses derived stable keys for Device Ownership Transfer or other flows. CM_HMAC
-
ECDSA384_SIGNATURE_VERIFY: This command verifies ECDSA384 signatures for Device Ownership Transfer or other flows. ECDSA384_SIGNATURE_VERIFY
-
MLDSA87_SIGNATURE_VERIFY: This command verifies MLDSA87 signatures for Device Ownership Transfer or other flows. MLDSA87_SIGNATURE_VERIFY
-
CM_RANDOM_GENERATE: This command returns random numbers from Caliptra's RNG for Device Ownership Transfer or other flows. CM_RANDOM_GENERATE
-
CM_SHA: This ROM-only command (ROM 2.0.1+ only) computes a SHA-384 or SHA-512 hash of input data in a single operation. This is useful for MCU ROM to verify signatures and hashes against Vendor PK hash without needing its own hash implementation. Unlike the runtime CM_SHA_INIT/CM_SHA_UPDATE/CM_SHA_FINAL commands, this is a one-shot operation that does not support streaming or contexts. See CM_SHA below for details.
-
GET_LDEV_ECC384_CERT: This command fetches an LDevID ECC384 certificate signed by the ECC384 IDevID private key. GET_LDEV_ECC384_CERT
-
GET_LDEV_MLDSA87_CERT: This command fetches an LDevID MLDSA87 certificate signed by the MLDSA87 IDevID private key. GET_LDEV_MLDSA87_CERT
-
INSTALL_OWNER_PK_HASH: This command saves the owner public key hash to persistent data. INSTALL_OWNER_PK_HASH
-
ZEROIZE_UDS_FE
Zeroizes (sets to 0xFFFFFFFF) the UDS (Unique Device Secret) and/or FE (Field Entropy) partitions in the OTP fuse controller. This command is typically used during device decommissioning or ownership transfer flows.
The command accepts a flags field where each bit controls a specific partition. Multiple partitions can be zeroized in a single command by setting multiple flag bits.
The zeroization process follows these steps for each partition:
- Clears the zeroization marker first to mask potential ECC errors during power failures
- Zeroizes the seed data
- Clears the partition digest
All operations are verified to return 0xFFFFFFFF before proceeding.
Command Code: 0x5A45_5546 ("ZEUF")
Table: ZEROIZE_UDS_FE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| flags | u32 | Partition flags. See ZEROIZE_UDS_FE_FLAGS below. |
Table: ZEROIZE_UDS_FE_FLAGS input flags
| Name | Value | Description |
|---|---|---|
| ZEROIZE_UDS_FLAG | 1 << 0 | Zeroize UDS partition |
| ZEROIZE_FE0_FLAG | 1 << 1 | Zeroize FE partition 0 |
| ZEROIZE_FE1_FLAG | 1 << 2 | Zeroize FE partition 1 |
| ZEROIZE_FE2_FLAG | 1 << 3 | Zeroize FE partition 2 |
| ZEROIZE_FE3_FLAG | 1 << 4 | Zeroize FE partition 3 |
Table: ZEROIZE_UDS_FE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| dpe_result | u32 | Result code, 0 on success. |
CM_SHA
This ROM-only command computes a SHA-384 or SHA-512 hash of input data in a single one-shot operation. This command is designed for MCU ROM to verify signatures and hashes (e.g., against Vendor PK hash) without requiring its own hash implementation.
Note: This command is only available in ROM. Runtime firmware should use the streaming CM_SHA_INIT, CM_SHA_UPDATE, and CM_SHA_FINAL commands instead, which support contexts and incremental hashing.
Command Code: 0x434D_5348 ("CMSH")
Table: CM_SHA input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| hash_algorithm | u32 | Hash algorithm: 1 = SHA-384, 2 = SHA-512. Value 0 is reserved and will return an error. |
| input_size | u32 | Size of input data in bytes. Maximum 262,132 bytes (256 KB minus 12-byte header overhead) in passive mode, and 16,372 bytes in subsystem mode for 2.1 (16 KB minus overhead). |
| input | u8[input_size] | Input data to hash. Variable size up to the mailbox capacity. |
Table: CM_SHA output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | FIPS status. 0 = approved mode. |
| data_len | u32 | Length of hash output in bytes. 48 for SHA-384, 64 for SHA-512. |
| hash | u8[data_len] | The computed hash value. Variable size based on algorithm. |
Downloading firmware image from mailbox
There are two modes in which the ROM executes: PASSIVE mode or SUBSYSTEM mode. Following is the sequence of the steps that are performed to download the firmware image from mailbox in PASSIVE mode.
- ROM asserts READY_FOR_FIRMWARE signal.
- Poll for the execute bit to be set. This bit is set as the last step to transfer the control of the command to the Caliptra ROM.
- Read the command register and ensure the command is FW_DOWNLOAD.
- Read the data length register and validate the value in it.
- Read N dwords from the mailbox DATAOUT register. Execute the command.
- Once the entire data is processed, clear the execute bit.
- This should be the last step. Clearing this bit transfers the control back to the originator of the command.
- On failure, a non-zero status code will be reported in the
CPTRA_FW_ERROR_NON_FATALregister
Following is the sequence of steps that are performed to download the firmware image into the mailbox in SUBSYSTEM mode.
ROM supports two commands for firmware download in SUBSYSTEM mode:
- RI_DOWNLOAD_FIRMWARE (Command Code:
0x5249_4644/ "RIFD"): Standard firmware download. After downloading and validating the firmware, the runtime will activate the MCU firmware immediately. - RI_DOWNLOAD_ENCRYPTED_FIRMWARE (Command Code:
0x5249_4645/ "RIFE"): Encrypted firmware download. Sets the boot mode toEncryptedFirmware, which signals to the runtime that the MCU firmware is encrypted and should not be activated until it has been decrypted using theCM_AES_GCM_DECRYPT_DMAcommand.
- On receiving the RI_DOWNLOAD_FIRMWARE or RI_DOWNLOAD_ENCRYPTED_FIRMWARE mailbox command, set the RI PROT_CAP2 register version to 1.1 and the
Agent Capabilityfield bits:Device IDDevice StatusPush C-image supportFlashless bootFIFO CMS support
- Set the RI DEVICE_STATUS_0 register,
Device Statusfield to 0x3 ('Recovery mode - ready to accept recovery image') andRecovery Reason Codefield to 0x12 ('Flashless/Streaming Boot (FSB)'). - Set the RI RECOVERY_STATUS register,
Device Recovery Statusfield to 0x1 ('Awaiting recovery image') andRecovery Image Indexfield to 0 (Firmware Image). - Loop on the
payload_availablebit in theDMA Status0register for the firmware image info to be available. - Read the image size from RI INDIRECT_FIFO_CTRL_1 register. Image size is in DWORDs.
- Initiate image download from the recovery interface to the mailbox sram:
a. Write the payload length to the DMA widget 'Byte Count' register.
b. Write the block size with a value of 256 to the DMA widget 'Block Size' register.
c. Write the source address to the DMA widget 'Source Address - Low' and 'Source Address - High' registers.
d. Acquire the mailbox lock.
e. Write DMA widget 'Control' register.
- Set
Read Routebits to 0x1 (AXI RD -> Mailbox) - Set
Read Addr fixedbit. - Set Bit0 (GO)
f. Read DMA widget
Status0register in a loop if `Busy' bit is 0. g. Image is downloaded into mailbox sram.
- Set
- Set RI
DEVICE_STATUSregister,Device Statusfield to 0x4 (Recovery Pending (waiting for activation)) - Loop on RI
RECOVERY_CTRLregisterActivate Recovery Imagefield to wait for processing the image. - Set RI
RECOVERY_STATUSregisterDevice Recovery Statusfield to 0x2 (Booting recovery image). - Validate the image per the Image Validation Process.
- Reset the
RECOVERY_CTRLregisterActivate Recovery Imagefield by writing 0x1. - If the validation is succesful, set the
DEVICE_STATUSregisterDevice Statusfield to 0x5 (Running Recovery Image ( Recover Reason Code not populated)) - If the validation fails, set the
RECOVERY_STATUSregisterDevice Recovery Statusfield to 0xc (Recovery failed) andDEVICE_STATUSregisterDevice Statusfield to 0xF (Fatal Error (Recover Reason Code not populated)). - Release the mailbox lock.
Image validation
See Firmware Image Validation Process.
Derivation of the key ladder for Stable Identity
Stable Identity calls for a secret that remains stable across firmware updates, but which can ratchet forward when major firmware vulnerabilities are fixed. Caliptra ROM implements this feature in terms of a "key ladder".
The key ladder is initialized from the LDevID CDI during cold-boot. The key ladder length is inversely related to the firmware's SVN. Each step of the ladder is an SVN-unique key. The key for SVN X can be obtained by applying a one-way cryptographic operation to the key for SVN X+1. In this manner, firmware with a given SVN can wield keys bound to its SVN or older, but cannot wield keys bound to newer SVNs.
To comply with FIPS, the one-way cryptographic operation used to compute keys is an SP 800-108 KDF.
When the key ladder is initialized at cold-boot, it is bound to the lifecycle state and debug-locked. This ensures that the keys of the ladder will change across lifecycle or debug state transtions.
Across update-resets, ROM tracks the minimum SVN that has run since cold-boot. It ensures that the ladder's length always corresponds to that minimum SVN. The key ladder can only be shortened (and thereby give access to newer SVNs' keys) by cold-booting into firmware with a newer SVN and re-initializing the ladder.
Cold-boot
ROM initializes a key ladder for the firmware. LDevID CDI in Key Vault Slot6 is used as an HMAC Key, and the data is a fixed string. The resultant MAC is stored in Slot 2.
KeyLadderContext = lifecycle state || debug_locked state
hmac512_kdf(KvSlot6, label: b"si_init", context: KeyLadderContext, KvSlot2)
Loop (MAX_FIRMWARE_SVN - (current firmware SVN)) times:
hmac512_kdf(KvSlot2, label: b"si_extend", context: None, KvSlot2)
Update-reset
During update-reset, the key ladder initialized at cold boot is lengthened if necessary, such that its length always corresponds with the minimum SVN since cold boot.
old_min_svn = [retrieved from data vault]
new_min_svn = min(old_min_svn, new_fw_svn)
[store new_min_svn in data vault]
Loop (`old_min_svn` - `new_min_svn`) times:
hmac512_kdf(KvSlot2, label: b"si_extend", context: None, KvSlot2)
Alias FMC DICE layer & PCR extension
Alias FMC Layer includes the measurement of the FMC and other security states. This layer is used to assert a composite identity which includes the security state, FMC measurement along with the previous layer identities.
Pre-conditions:
- LDevID CDI is stored in Key Vault Slot 6
- LDevID MLDSA Key Pair Seed is stored in Key Vault Slot 4
- LDevID ECDSA Private Key is stored in Key Vault Slot 5
- Firmware Image Bundle is successfully loaded and verified from the Mailbox
- ROM has following information from Firmware Image Bundle
- FMC_DIGEST - Digest of the FMC
- FW_SVN - SVN for the firmware
- MANUFACTURER_PK - Manufacturer Public Key(s) used to verify the firmware image bundle
- MANUFACTURER_PK_INDEX - Index of the MANUFACTURER_PK in the firmware image bundle
Actions:
-
PCR0 is the Current PCR. PCR 1 is the Journey PCR. PCR0 is cleared by ROM upon each cold and update resets, before it is extended with FMC measurements. PCR0 and PCR1 are locked for clear by the ROM on every reset. Subsequent layers may continue to extend PCR0 as runtime updates are performed.
pcr_clear(Pcr0) pcr_extend(Pcr0 && Pcr1, [ CPTRA_SECURITY_STATE.LIFECYCLE_STATE, CPTRA_SECURITY_STATE.DEBUG_ENABLED, FUSE_ANTI_ROLLBACK_DISABLE, VENDOR_ECC_PK_INDEX, FW_SVN, FW_FUSE_SVN (or 0 if `FUSE_ANTI_ROLLBACK_DISABLE`), VENDOR_PQC_PK_INDEX, PQC_KEY_TYPE, OWNER_PK_HASH_FROM_FUSES (0 or 1), ]) pcr_extend(Pcr0 && Pcr1, MANUFACTURER_PK) pcr_extend(Pcr0 && Pcr1, OWNER_PK) pcr_extend(Pcr0 && Pcr1, FMC_TCI) pcr_lock_clear(Pcr0 && Pcr1) -
CDI for Alias is derived from PCR0. For the Alias FMC CDI Derivation, LDevID CDI in Key Vault Slot6 is used as HMAC Key and contents of PCR0 are used as data. The resultant MAC is stored back in Slot 6.
Pcr0Measurement = pcr_read(Pcr0)hmac512_kdf(KvSlot6, label: b"alias_fmc_cdi", context: Pcr0Measurement, KvSlot6) -
Derive Alias FMC ECDSA Key Pair using CDI in Key Vault Slot 6 and store the generated private key in Key Vault Slot 7.
AliasFmcSeedEcdsa = hmac512_kdf(KvSlot6, b"fmc_alias_ecc_key", KvSlot3)AliasFmcPubKeyEcdsa = ecc384_keygen(KvSlot3, KvSlot7)kv_clear(KvSlot3)Derive the Alias FMC MLDSA Key Pair using CDI in Key Vault Slot 6 and store the key pair generation seed in Key Vault Slot 8.
AliasFmcSeedMldsa = hmac512_kdf(KvSlot6, b"fmc_alias_mldsa_key", KvSlot8)AliasFmcPubKeyMldsa = mldsa87_keygen(KvSlot8) -
Store and lock (for write) the FMC ECDSA and MLDSA Public Keys in the DCCM datavault.
-
Generate the
To Be SignedDER Blob of the ECDSA Alias FMC Certificate.AliasFmcTbsEcdsa = gen_cert_tbs(ALIAS_FMC_CERT, LDevIdPubKeyEcdsa, AliasFmcPubKeyEcdsa) -
Sign the Alias FMC
To Be SignedDER Blob with the LDevId ECDSA Private Key in Key Vault Slot 5.AliasFmcTbsDigestEcdsa = sha384_digest(AliasFmcTbsEcdsa)AliasFmcTbsCertSigEcdsa = ecc384_sign(KvSlot5, AliasFmcTbsDigestEcdsa) -
Clear the LDevId Private Key in Key Vault Slot 5.
kv_clear(KvSlot5) -
Verify the signature of Alias FMC
To Be SignedECDSA Blob.Result = ecc384_verify(LDevIdPubKeyEcdsa, AliasFmcDigestEcdsa, AliasFmcTbsCertSigEcdsa) -
Generate the
To Be SignedDER Blob of the MLDSA Alias FMC Certificate.AliasFmcTbsMldsa = gen_cert_tbs(ALIAS_FMC_CERT, LDevIdPubKeyMldsa, AliasFmcPubKeyMldsa) -
Sign the Alias FMC
To Be SignedDER Blob with the LDevId MLDSA Private Key generated from the seed in Key Vault Slot 4.AliasFmcTbsDigestMldsa = sha512_digest(AliasFmcTbsMldsa)AliasFmcTbsCertSigMldsa = mldsa87_sign(KvSlot4, AliasFmcTbsDigestMldsa) -
Clear the LDevId MLDSA key generation seed in Key Vault Slot 4.
kv_clear(KvSlot4) -
Verify the signature of Alias FMC
To Be SignedMLDSA Blob.Result = mldsa87_verify(LDevIdPubKeyMldsa, AliasFmcDigestMldsa, AliasFmcTbsCertSigMldsa) -
Store and lock (for write) the Alias FMC Certificate ECDSA and MLDSA Signatures in the DCCM datavault.
-
Lock critical state needed for warm and update reset in the DCCM datavault.
dccm_dv_store(FMC_DIGEST, lock_for_wr)dccm_dv_store(FW_SVN, lock_for_wr)dccm_dv_store(FUSE_OWNER_PK_HASH, lock_for_wr)dccm_dv_store(MANUFACTURER_ECC_PK_INDEX, lock_for_wr)dccm_dv_store(MANUFACTURER_PQC_PK_INDEX, lock_for_wr)dccm_dv_store(ROM_COLD_BOOT_STATUS, lock_for_wr)Note: A value of 0x140 is stored on a successful cold boot.
Post-conditions:
- Vault state as follows:
| Slot | Key Vault |
|---|---|
| 0 | Stable identity root IDevID secret (64 bytes) |
| 1 | Stable identity root LDevID secret (64 bytes) |
| 6 | Alias FMC CDI (48 bytes) |
| 7 | Alias FMC Private Key - ECDSA (48 bytes) |
| 8 | Alias FMC Key Pair Seed - MLDSA (32 bytes) |
| DCCM datavault |
|---|
| πIDevID Cert ECDSA Signature |
| πIDevID ECDSA Pub Key |
| πIDevID Cert MLDSA Signature |
| πIDevID MLDSA Pub Key |
| πLDevID Cert ECDSA Signature R |
| πLDevID Cert ECDSA Signature S |
| πLDevID Cert MLDSA Signature |
| πLDevID Pub Key ECDSA X |
| πLDevID Pub Key ECDSA Y |
| πLDevID Pub Key MLDSA |
| πAlias FMC Cert ECDSA Signature R |
| πAlias FMC Cert ECDSA Signature S |
| πAlias FMC Cert MLDSA Signature |
| πFW SVN |
| πROM Cold Boot Status |
| πFMC Entry Point |
| πManufacturer ECDSA Public Key Index |
| πManufacturer PQC Public Key Index |
| πAlias FMC ECDSA Pub Key X |
| πAlias FMC ECDSA Pub Key Y |
| πAlias FMC MLDSA Pub Key |
| πFMC Digest |
| πOwner PK Hash |
Locking of memory regions and registers
ROM locks the following entities to prevent any updates:
-
Cold Reset Unlockable values: These values are unlocked on a Cold Reset:
- FMC TCI
- FMC Entry Point
- Owner Pub Key Hash
- Ecc Vendor Pub Key Index
- PQC Vendor Pub Key Index
- ROM Cold Boot Status
-
Warm Reset unlockable values: These values are unlocked on a Warm or Cold Reset:
- RT TCI
- RT Entry Point
- FW SVN
- Manifest Addr
- ROM Update Reset Status
-
PCR values
- FMC_CURRENT
- FMC_JOURNEY
- STASH_MEASUREMENT
-
ICCM
Launch FMC
The ROM initializes and populates the Firmware Handoff Table (FHT) to relay essential parameters to the FMC. The format of the FHT is documented here. Upon successful population, the ROM transfers execution control to the FMC.
Warm reset flow
ROM does not perform any DICE derivations or firmware validation during warm reset.
Initialization
ROM performs the same initialization sequence as specified here
Locking of memory regions and registers
ROM locks the following entities to prevent any updates:
-
Warm Reset unlockable values:
- RT TCI
- RT Entry Point
- FW SVN
- Manifest Addr
- ROM Update Reset Status
-
PCR values
- FMC_CURRENT
- FMC_JOURNEY
- STASH_MEASUREMENT
-
ICCM
Update reset flow
*(Note: Please note that Image validation for the update reset flow has some differences as compared to the cold boot flow. Please refer to the Image Validation Section for further details.)
Unknown/spurious reset flow
Initialization
ROM performs the same initialization sequence as specified here
Error handling
The ROM executes the following operations:
- Updates the
cptra_fw_error_fatalandcptra_fw_error_non_fatalregisters with the error code ROM_UNKNOWN_RESET_FLOW (0x01040020) error code. - Zeroizes the following cryptographic hardware modules:
- Ecc384
- Hmac384
- Sha256
- Sha384
- Sha2-512-384Acc
- KeyVault
- Stops the WatchDog Timer.
- Enters an infinite loop, awaiting a reset.
Firmware image validation process
The basic flow for validating the firmware involves the following:
- Validating the manufacturer key descriptors in the preamble.
- Validating the active manufacturer keys with the corresponding hash in the key descriptors.
- Validating the owner key descriptors in the preamble.
- Validating the owner keys with the hash in the key descriptors.
- Validating the active manufacturer keys against the key revocation fuses.
- Validating the Manifest Header using the active manufacturer keys against the manufacturer signatures.
- Validating the Manifest Header using the owner keys against the owner signatures.
- On the completion of these validations, it is assured that the header portion is authentic.
- Loading the FMC and Rutime (RT) TOC entries from the mailbox.
- Validating the TOCs against the TOC hash in the header.
- On successful validation, it is assured that the TOCs are valid. The next step is to use the Hash entry in the TOCs to validate the image sections.
- Downloading the FMC Image portion of the firmware Image.
- Validating the FMC Image against the hash in the TOC entry for the FMC.
- If this is a cold reset, the FMC version number should be stored in a register.
- Downloading the RT Image part of the firmware Image.
- Validating the RT Image against the hash in the TOC entry for the RT.
- On the successful completion of these validations, the entire image is validated.
- Indicating to the SOC of validation success by completing the mailbox command.
- On validation failure, reporting the appropriate error in the
CPTRA_FW_ERROR_FATALregister and invoking the error handler
Overall validation flow
Pre-conditions
The following are the pre-conditions that should be satisfied:
- Caliptra has transitioned through the BOOTFSM and all the fuses that are required for the validation are already populated by SOC.
- The FUSES programmed by the soc are
- fuse_vendor_pk_hash : This fuse contains the hash of the manufacturer key descriptors present in the preamble.
- fuse_ecc_revocation : This is the bitmask of the ECC keys which are revoked.
- fuse_lms_revocation : This is the bitmask of the LMS keys which are revoked.
- fuse_mldsa_revocation : This is the bitmask of the MLDSA keys which are revoked.
- fuse_owner_pk_hash : The hash of the owner public keys in preamble.
- fuse_firmware_svn : Used in FW validation to make sure that the firmware image's SVN is good.
- fuse_pqc_key_type: This bitmask specifies the enabled PQC key type for firmware validation, indicating either MLDSA or LMS.
- The SOC has written the data to the mailbox.
- The SOC has written the data length in the DLEN mailbox register.
- The SOC has put the FW_DOWNLOAD command in the command register.
- The SOC has set the execute bit in the mailbox execute register.
( NOTE: At this time the interrupts are not enabled. Writing a execute bit will not generate an interrupt. The validation and update flow will need to be invoked externally.)
Preamble validation: Validate the manufacturing keys
- Load the preamble bytes from the mailbox.
- Based on the firmware image type, the image includes an ECC key descriptor and either an LMS or MLDSA key descriptor within the preamble. The ECC descriptor encapsulates up to four ECC public key hashes, the LMS descriptor up to 32 public key hashes, and the MLDSA descriptor up to four MLDSA public key hashes.
- The firmware image, depending on its type, incorporates an ECC key and either an LMS or MLDSA manufacturing public key within the preamble. These constitute the active public keys.
- The fuse_vendor_pk_hash fuse holds the SHA2-384 hash of the ECC, and LMS or MLDSA manufacturing key descriptors.
- The key descriptors are validated by generating a SHA2-384 hash of the ECC and LMS or MLDSA key descriptors and comparing it against the hash stored in the fuse. If the hashes do not match, the image validation fails.
- Upon a successful hash match, the ECC, and LMS or MLDSA key descriptors are deemed valid.
- Subsequently, the active manufacturer public keys are validated against one of the hashes in the key descriptors. The specific hash for comparison is identified by the active key indices.
Preamble validation: Manufacturing key validation
- fuse_ecc_revocation serves as the bitmask for revoking ECC keys.
- If bit-n is set, the nth key is disabled. All other higher bits that are zeros indicate the keys are still enabled.
- If all the bits are zeros, all ECC keys remain enabled.
- Ensure that the Active Key Index in the preamble is not disabled by the fuse_ecc_revocation fuse.
- If the key is disabled, the validation process fails.
- Repeat the above procedure for LMS or MLDSA keys using the fuse_lms_revocation or fuse_mldsa_revocation fuses, respectively, for key revocation.
Preamble validation: Validate the Owner key
- The preamble includes a designated slot for the owner ECC key and a slot for either LMS or MLDSA keys.
- The fuse_owner_pk_hash contains the hash of the owner public keys.
- The validation process for owner public keys involves generating a SHA2-384 hash from the owner public keys within the preamble and comparing it to the hash stored in the fuse_owner_pk_hash register.
- If the computed hash matches the value in fuse_owner_pk_hash, the owner public keys are deemed valid.
- If there is a hash mismatch, the image validation process fails.
Preamble validation steps
Header validation
- Retrieve the header portion of the firmware image from the mailbox.
- Note that the header is the sole signed component, featuring two distinct signatures pairs.
- The first signature pair is generated using the active ECC and LMS or MLDSA manufacturing keys.
- The second signature pair is generated using the owner ECC and LMS or MLDSA public keys.
- To validate the header:
- Compute the SHA2-384 hash of the header.
- Verify the ECC manufacturer signature in the preamble against the computed hash.
- If the ECC manufacturer signature is invalid, fail the validation process. If the ECC manufacturer signature is valid, apply the same procedure using the LMS or MLDSA manufacturer key.
- Similarly, utilize the precomputed hash to verify the signature with the ECC owner public key. Repeat the process using the LMS or MLDSA owner key.
Header validation steps
Table of contents validation
- At this point both the Preamble and the Header have been validated.
- Load the TOC entries (FMC TOC and RT TOC) from the mailbox.
- Compute the SHA2-384 hash of the complete TOC data.
- Compare the computed TOC hash with the hash embedded in the Header.
- If the hashes match, the TOC data is validated.
- Ensure that Fw.Svn is greater than or equal to Fuse.Svn.
*(Note: Same SVN Validation is done for the FMC and RT)
Table of contents validation steps
Validating image sections
- Upon successful validation of the TOC, each image section corresponding to the TOC requires validation.
- The hash for each image section is encapsulated within the TOC data.
- Retrieve the FMC Image section. The offset and size of the section are specified in the TOC.
- Compute the SHA2-384 hash for the FMC image section.
- Compare the computed hash with the hash specified in the FMC TOC.
- If the hashes match, the FMC image section is considered validated. If the hashes do not match, the image is rejected.
- Retrieve the RT Image section from the mailbox. The offset and size of the section are specified in the TOC.
- Compute the SHA2-384 hash for the RT image section.
Compare the computed hash with the hash specified in the RT TOC.
- If the hashes match, the RT image section is considered validated. If the hashes do not match, the image is rejected.
Image section validation steps
Differences in operating mode of the validation code
- The validation code operates in three modes.
- Cold Boot Mode
- Warm Boot Mode
- Update Reset Mode
- Cold Boot Mode
- Validation of the entire image is done using the steps described above.
- Save the hash of the FMC portion of the image in a separate register.
- Copy the FMC and RT image's text and data section in the appropriate ICCM and DCCM memory regions.
- The data vault is saved with the following values:-
- LDevId Dice ECDSA Signature.
- LDevId Dice MLDSA Signature.
- LDevId Dice ECDSA Public Key.
- LDevId Dice MLDSA Public Key.
- Alias FMC Dice ECDSA Signature.
- Alias FMC Dice MLDSA Signature.
- Alias FMC Public ECDSA Key.
- Alias FMC Public MLDSA Key.
- Digest of the FMC part of the image.
- Digest of the ECC and LMS or MLDSA owner public keys portion of preamble.
- FW SVN.
- ROM Cold Boot Status.
- FMC Entry Point.
- ECC Vendor public key index.
- LMS or MLDSA Vendor public key index.
- Warm Boot Mode
- In this mode there is no validation or load required for any parts of the image.
- All the contents of ICCM and DCCM are preserved.
- Update Reset Mode
- The image is exactly the same as the initial image which was verified on the cold boot, except that the RT part of the image is changed.
- We need to validate the entire image exactly as described in the cold boot flow. In addition to that, also validate the image to make sure that no other part (except the RT image section) is altered.
- The validation flow will look like the following:
- Validate the preamble exactly like in cold boot flow.
- Validate the vendor public key indices from the values in data vault (value saved during cold boot). Fail the validation if there is a mismatch. This is done to make sure that the key being used is the same key that was used during cold boot.
- Validate the owner public key digest against the owner public key digest in data vault (value saved during cold boot). This ensures that the owner keys have not changed since last cold boot.
- Validate the header exactly like in cold boot.
- Validate the toc exactly like in cold boot.
- We still need to make sure that the digest of the FMC which was stored in the data vault register at cold boot still matches the FMC image section.
- Store the minimum firmware SVN that has run since cold-boot in the data vault.
- Ratchet the key ladder if necessary.
- Validate the preamble exactly like in cold boot flow.
- If validation fails during ROM boot, the new RT image will not be copied from the mailbox. ROM will boot the existing FMC/Runtime images. Validation errors will be reported via the CPTRA_FW_ERROR_NON_FATAL register.
Fake ROM
Fake ROM is a variation of the ROM intended to be used in the verification/enabling stages of development. The purpose is to greatly reduce the boot time for pre-Si environments by eliminating certain steps from the boot flow. Outside of these omissions, the behavior is intended to be the same as normal ROM.
Fake ROM is only available in production mode if the enable bit is set in the CPTRA_DBG_MANUF_SERVICE_REG (see section above).
Differences from normal ROM:
Fake ROM reduces boot time by doing the following:
- Skipping the DICE cert derivation and instead providing a static, "canned" cert chain for LDEV and FMC Alias
- Skipping the known answer tests (KATs)
- Skipping verification of the FW image received - This can optionally still be performed, see CPTRA_DBG_MANUF_SERVICE_REG
How to use:
- Fake ROM is provided in the release along with the normal collateral.
- The image builder exposes the argument "fake" that can be used to generate the fake versions
To fully boot to runtime, the fake version of FMC should also be used. Details can be found in the FMC readme.
Optional UART via Generic Output Wires
For debugging and development purposes, the ROM (when built with the emu feature) can output log messages using the CPTRA_GENERIC_OUTPUT_WIRES[0] register as an optional UART interface. This provides a simple mechanism for observing ROM execution without requiring a dedicated UART peripheral.
Protocol
The UART output uses the following encoding in CPTRA_GENERIC_OUTPUT_WIRES[0]:
| Bits | Description |
|---|---|
| [7:0] | Character data (ASCII printable characters 0x20-0x7E, newline 0x0A, tab 0x09). Non-printable characters are replaced with 0xFE. |
| [8] | Toggle bit. This bit is toggled every time a new character is written, allowing external observers to detect new characters without introspecting internal signals. |
| [31:9] | Reserved |
Usage
- The SOC or testbench can monitor
CPTRA_GENERIC_OUTPUT_WIRES[0]and detect new characters by observing changes to bit 8. - When bit 8 changes, the new character is available in bits [7:0].
- If desired, this can be wired to a FIFO or other mechanism for output.
9248d79
SOC Manifest
The Caliptra SOC manifest has two main components: Preamble and Image Metadata Collection
Preamble
The Preamble section contains the authorization manifest ECC and PQC (LMS or MLDSA) public keys of the vendor and the owner. These public keys correspond to the private keys that sign the Image Metadata Collection (IMC) section. Those signatures are also stored in the Preamble. The Caliptra firmware's ECC and PQC private keys endorse the manifest's public keys, and these endorsements (signatures) are part of the Preamble as well.
Note: All fields are little endian unless specified
| Field | Size (bytes) | Description |
|---|---|---|
| Manifest Marker | 4 | Magic number marking the start of the manifest. The value must be 0x324D5441 ('ATM2' in ASCII). |
| Manifest Size | 4 | Size of the full manifest structure in bytes. |
| Version | 4 | Manifest version. The current version is 0x00000002. |
| SVN | 4 | Security Version Number used for anti-rollback. The maximum value is vendor-defined and is limited by the maximum size of the Caliptra fuse allocated for anti-rollback. |
| Flags | 4 | Manifest feature flags. Bit 0 β Vendor Signature Required. If set, the vendor public keys (ECC and PQC) will be used to verify signatures signed with the vendor private keys. If clear, vendor signatures are not used for verification. Bits 1β31 β Reserved. |
| Vendor ECC Public Key | 96 | Vendor ECC P-384 public key used to verify the IMC signature and endorse PQC keys. X-Coordinate: 48 bytes Y-Coordinate: 48 bytes. |
| Vendor PQC Public Key (LMS or MLDSA) | 2592 | Vendor PQC public key used to verify the IMC signature and to endorse the vendor measurement keys. This field is sized to support MLDSA87 (2592-byte public key). When: β’ MLDSA87 is used, the field holds the full 2592-byte MLDSA87 public key. β’ LMS (e.g., LMS-SHA192-H15) is used, the LMS public key (e.g., 48 bytes) is stored at the beginning of the field and the remaining bytes must be zeroed. |
| Vendor ECC Signature | 96 | Vendor ECDSA P-384 signature over the Preamble fields that are covered by policy, typically including Version, SVN, Flags, and vendor ECC/PQC public keys, hashed using SHA2-384. R-Coordinate: 48 bytes S-Coordinate: 48 bytes. |
| Vendor PQC Signature (LMS or MLDSA) | 4628 | Vendor PQC signature over the same Preamble fields as the ECC signature. This field is sized to support the MLDSA87 signature (4628 bytes). When: β’ MLDSA87 is used, the entire field holds the MLDSA87 signature (per FIPS-204 definition, up to 4628 bytes). β’ LMS (e.g., LMS-SHA192-H15 / LMOTS-SHA192-W4) is used, the LMS signature (e.g., ~1620 bytes) is stored at the beginning and the remaining bytes must be zeroed. If PQC validation is not required, this field must be zeroed. |
| Owner ECC Public Key | 96 | Owner ECC P-384 public key used to verify the IMC signature and endorse PQC keys on behalf of the platform owner. X-Coordinate: 48 bytes Y-Coordinate: 48 bytes. |
| Owner PQC Public Key (LMS or MLDSA) | 2592 | Owner PQC public key used to verify the IMC signature and to endorse owner measurement keys. Same encoding rules as Vendor PQC Public Key (LMS or MLDSA): MLDSA87 fills the field; LMS occupies the beginning and zero-pads the rest. |
| Owner ECC Signature | 96 | Owner ECDSA P-384 signature over the Preamble fields that are covered by policy for the owner (Version, SVN, Flags, owner ECC/PQC keys, etc.), hashed using SHA2-384. R-Coordinate: 48 bytes S-Coordinate: 48 bytes. |
| Owner PQC Signature (LMS or MLDSA) | 4628 | Owner PQC signature over the same Preamble fields as the Owner ECC signature. Same layout rules as Vendor PQC Signature (LMS or MLDSA) (MLDSA87 uses full field; LMS uses prefix + zero padding). If PQC validation is not required, this field must be zeroed. |
| IMC Vendor ECC Signature | 96 | Vendor ECDSA P-384 signature over the Image Metadata Collection (IMC), hashed using SHA2-384. R-Coordinate: 48 bytes S-Coordinate: 48 bytes. |
| IMC Vendor PQC Signature (LMS or MLDSA) | 4628 | Vendor PQC signature over the IMC. Uses the same encoding as Vendor PQC Signature (LMS or MLDSA), but the signed message is the serialized IMC instead of the Preamble. If PQC validation is not required, this field must be zeroed. |
| IMC Owner ECC Signature | 96 | Owner ECDSA P-384 signature over the IMC, hashed using SHA2-384. R-Coordinate: 48 bytes S-Coordinate: 48 bytes. |
| IMC Owner PQC Signature (LMS or MLDSA) | 4628 | Owner PQC signature over the IMC. Same encoding rules as the other PQC signature fields (LMS or MLDSA; unused bytes zero-padded). If PQC validation is not required, this field must be zeroed. |
Image Metadata Collection
The Image Metadata Collection (IMC) is a collection of Image Metadata Entries (IMEs). Each IME has a hash that matches a SOC image. The manifest vendor and owner private keys sign the IMC. The Preamble holds the IMC signatures. The manifest IMC vendor signatures are optional and are validated only if the Flags Bit 0 = 1. Up to 127 image hashes are supported.
| Field | Size (bytes) | Description |
|---|---|---|
| Image Metadata Entry (IME) Count | 4 | Number of IME(s) in the IMC. |
| Image Metadata Entry (N) | Variable | List of Image Metadata Entry structures |
Image Metadata Entry
| Field | Size (bytes) | Description |
|---|---|---|
| Image Hash | 48 | SHA2-384 hash of a SOC image. |
| Image Identifier | 4 | Unique value selected by the vendor to distinguish between images. |
| Component Id | 4 | Identifies the image component to be loaded. This corresponds to the ComponentIdentifier field defined in the DMTF PLDM Firmware Update Specification (DSP0267). |
| Flags | 4 | Image-specific flags. Bit 0: If set, the image hash will not be verified; otherwise, the metadata image hash will be compared against the calculated hash of the image. Bit 1: If set, indicates that the image is an MCU Runtime image; otherwise, it indicates a SOC image. Bits 8β14: Firmware execution control bit mapped to this image. Other bits: reserved. |
| Image Load Address High | 4 | High 4 bytes of the 64-bit AXI address where the image will be loaded for verification and execution. |
| Image Load Address Low | 4 | Low 4 bytes of the 64-bit AXI address where the image will be loaded for verification and execution. |
| Staging Address High | 4 | High 4 bytes of the 64-bit AXI address where the image will be temporarily written during firmware update download and verification. |
| Staging Address Low | 4 | Low 4 bytes of the 64-bit AXI address where the image will be temporarily written during firmware update download and verification. |
9248d79
Caliptra - FMC Specification v2.0
Spec Version: 0.9
Scope
Caliptra is an open-source Hardware Root of Trust for Measurement (RTM). This document is the architecture specification for Caliptra First Mutable Code (FMC). As an architecture specification for FMC, this document describes the following topics:
- Provide high level requirements
- Describe FMC load and measurement flow
- Describe FMC functionality
- Define FMC boot flows
Glossary
| Term | Description |
|---|---|
| DCCM | Data Closely Coupled Memory |
| DICE | Device Identifier Composition Engine |
| FHT | Firmware Handoff Table |
| FMC | First Mutable Code |
| FW | Firmware |
| ICCM | Instruction Closely Coupled Memory |
| PCR | Platform Configuration Register |
| RoT | Root of Trust |
| RT | Runtime |
| RTM | Root of Trust for Measurement |
| TCI | Trusted Component Identifier |
| SVN | Security Version Number |
Overview
First Mutable Code (FMC) is the first field-updatable firmware module in the Caliptra boot sequence. It is loaded, cryptographically verified, and executed by the Caliptra ROM.
Pre-conditions / assumptions
It is assumed that the Caliptra ROM has already performed a series of steps to prepare the Caliptra environment before calling the FMC entry point. The following is a brief overview of those expectations. Further details can be found in the Caliptra ROM Specification.
- ROM is responsible for initializing its ROM-based FIPS Crypto Module
(Note that this requirement is dependent on the chosen FIPS boundary. It only applies if there is a discrete FIPS ROM module that is isolated from the rest of the ROM. This is not expected to be the case for the first generation of Caliptra.) - ROM is responsible for locating the image containing all of Caliptraβs mutable firmware and loading it into ICCM.
- ROM is responsible for authentication of the Manifest and each individual FW Module loaded to ICCM.
- ROM is responsible for ensuring that the Anti-Rollback Protection is enforced for all mutable firmware modules.
- ROM is responsible for creating Caliptraβs initial DICE identity and extending it with measurements of the FMC Module.
- ROM jumps to the Caliptra FMC entry point.
At the time the Caliptra FMC entry point is executed, the Caliptra memory space will look like one of the following diagrams (dependent upon selected FIPS Crypto boundary):
Current POR: All Caliptra FW in FIPS boundary
Alternate: Caliptra ROM and FW each have discrete FIPS modules
Alternate: Caliptra ROM implements FIPS Module used by all other components
FMC responsibilities
FMC can be thought of as essentially a small, mutable extension of the ROM. Its primary purpose is to bridge execution from the immutable ROM code, prepare the environment for the main runtime firmware, and then execute that runtime firmware. As such, the code should be kept to the bare minimum needed to perform that task. βFeature-creepβ in this area is undesirable, and all efforts shall be made to avoid it.
- FMC must initialize the FW-based FIPS Crypto Module that is loaded alongside it. This initialization must be completed before any cryptographic operations can
be performed.
(Note that this requirement is dependent on the chosen FIPS boundary. It only applies if there is a discrete FIPS firmware module that is loaded separately from the FMC FW module. This is not expected to be the case for the first generation of Caliptra.) - FMC must measure the Runtime Firmware Module using services from the FIPS Crypto Module.
- FMC must extend the Caliptra DICE identity to the Runtime Firmware Module using FIPS Crypto services, generating artifacts CDIRT, AliasKeyPairRT, and certifying PublicKeyRT.
- At any time during its flow, the FMC MAY be required to execute a workaround for an RTL or ROM bug that was discovered after Caliptra hardware was frozen. The nature, feasibility, and timing of such a workaround will be dependent on the specific details of the bug.
- FMC must make the CDIRT, AliasKeyPairRT, and CertRT available to the Runtime Firmware Module, while making its own CDIFMC and PrivateKeyFMC unavailable.
- FMC must execute the Runtime Firmware Module.
Firmware handoff table
The Firmware Handoff Table is a data structure that is resident at a well-known location in DCCM. It is initially populated by ROM and modified by FMC as a way to pass parameters and configuration information from one firmware layer to the next.
Table revisions with the same Major Version must remain backward compatible (i.e. fields may be added to the end of the table, or fields may be deprecated, but fields may not be changed or removed). Table revisions with different Major Versions may or may not be compatible.
Note: All fields are little-endian unless otherwise specified.
| Field | Size (bytes) | Written By | Description |
|---|---|---|---|
| fht_marker | 4 | ROM | Magic Number marking start of FHT. Value must be 0x54484643, βCFHTβ when viewed as little-endian ASCII. |
| fht_major_ver | 2 | ROM | Major version of FHT. |
| fht_minor_ver | 2 | ROM, FMC | Minor version of FHT. Initially written by ROM but may be changed to a higher version by FMC. |
| manifest_load_addr | 4 | ROM | Physical base address of Manifest in DCCM SRAM. |
| fips_fw_load_addr_hdl | 4 | ROM | Handle of base address of FIPS Module in ROM or ICCM SRAM. May be 0xFF if there is no discrete module. |
| fmc_cdi_kv_hdl | 4 | ROM | Handle of FMC CDI value in the Key Vault. Value of 0xFF indicates not present. |
| fmc_priv_key_ecdsa_kv_hdl | 4 | ROM | Handle of FMC Alias ECDSA Private Key in the Key Vault. |
| fmc_keypair_seed_mldsa_kv_hdl | 4 | ROM | Handle of FMC Alias MLDSA Key Pair Generation Seed in the Key Vault. |
| fmc_pub_key_ecdsa_x_dv_hdl | 4 | ROM | Handle of FMC Alias ECDSA Public Key X Coordinate in the DCCM datavault. |
| fmc_pub_key_ecdsa_y_dv_hdl | 4 | ROM | Handle of FMC Alias ECDSA Public Key Y Coordinate in the DCCM datavault. |
| fmc_pub_key_mldsa_dv_hdl | 4 | ROM | Handle of FMC Alias MLDSA Public Key in the DCCM datavault. |
| fmc_cert_sig_ecdsa_r_dv_hdl | 4 | ROM | Handle of FMC Certificate ECDSA Signature R Component in the DCCM datavault. |
| fmc_cert_sig_ecdsa_s_dv_hdl | 4 | ROM | Handle of FMC Certificate ECDSA Signature S Component in the DCCM datavault. |
| fmc_cert_sig_mldsa_dv_hdl | 4 | ROM | Handle of FMC Certificate MLDSA Signature in the DCCM datavault. |
| rt_cdi_kv_hdl | 4 | FMC | Handle of RT CDI value in the Key Vault. |
| rt_priv_key_ecdsa_kv_hdl | 4 | FMC | Handle of RT Alias ECDSA Private Key in the Key Vault. |
| rt_keygen_seed_mldsa_kv_hdl | 4 | FMC | Handle of RT Alias MLDSA Key Generation Seed in the Key Vault. |
| ldevid_tbs_ecdsa_addr | 4 | ROM | Local Device ID ECDSA TBS Address. |
| fmcalias_tbs_ecdsa_addr | 4 | ROM | FMC Alias TBS ECDSA Address. |
| ldevid_tbs_mldsa_addr | 4 | ROM | Local Device ID MLDSA TBS Address. |
| fmcalias_tbs_mldsa_addr | 4 | ROM | FMC Alias TBS MLDSA Address. |
| ldevid_tbs_ecdsa_size | 2 | ROM | Local Device ID ECDSA TBS Size. |
| fmcalias_tbs_ecdsa_size | 2 | ROM | FMC Alias TBS ECDSA Size. |
| ldevid_tbs_mldsa_size | 2 | ROM | Local Device ID MLDSA TBS Size. |
| fmcalias_tbs_mldsa_size | 2 | ROM | FMC Alias TBS MLDSA Size. |
| pcr_log_addr | 4 | ROM | PCR Log Address. |
| pcr_log_index | 4 | ROM | Last empty PCR log entry slot index. |
| meas_log_addr | 4 | ROM | Measurement Log Address. |
| meas_log_index | 4 | ROM | Last empty Measurement log entry slot index. |
| fuse_log_addr | 4 | ROM | Fuse Log Address. |
| rt_dice_pub_key_ecdsa | 96 | FMC | RT Alias DICE ECDSA Public Key. |
| rt_dice_pub_key_mldsa_dv_hdl | 4 | FMC | RT Alias DICE MLDSA Public Key in the DCCM datavault. |
| rt_dice_sign_ecdsa | 96 | FMC | RT Alias DICE ECDSA signature. |
| rt_dice_sign_mldsa_dv_hdl | 4 | FMC | RT Alias DICE MLDSA signature in the DCCM datavault. |
| ldevid_cert_sig_ecdsa_r_dv_hdl | 4 | ROM | Handle of LDevId Certificate ECDSA Signature R Component in the DCCM datavault. |
| ldevid_cert_sig_ecdsa_s_dv_hdl | 4 | ROM | Handle of LDevId Certificate ECDSA Signature S Component in the DCCM datavault. |
| ldevid_cert_sig_mldsa_dv_hdl | 4 | ROM | Handle of LDevId Certificate MLDSA Signature in the DCCM datavault. |
| idev_dice_pub_key_ecdsa | 96 | ROM | Initial Device ID ECDSA Public Key. |
| idev_dice_pub_key_mldsa_dv_hdl | 4 | ROM | Initial Device ID MLDSA Public Key in the DCCM datavault. |
| rom_info_addr | 4 | ROM | Address of ROM Info struct describing the ROM digest and git commit. |
| rtalias_tbs_ecdsa_size | 2 | FMC | RT Alias ECDSA TBS Size. |
| rtalias_tbs_mldsa_size | 2 | FMC | RT Alias MLDSA TBS Size. |
| reserved | 1588 | Reserved for future use. |
FHT is currently defined to be 2048 bytes in length.
fht_marker
This is a "magic number" used to identify the start of the table, allowing the FMC or RT firmware modules to determine that the FHT has been populated. The expected value 0x54484643 will appear as ASCII βCFHTβ when viewed as a hex dump.
fht_major_ver & fht_minor_ver
The Major and Minor version numbers of the Firmware Handoff Table. All FHT versions with the same Major version number must remain backward compatible. Therefore, fields must remain at constant offsets, and no fields may be redefined. It is possible to deprecate existing fields or define new fields within the reserved space at the end of the table by incrementing the Minor version number
For example, a Caliptra ROM is be frozen with FHT version 1.0. During later stages of development, it is found that an additional 4 byte data field must be passed from FMC to Runtime. During boot, the ROM will populate the FHT as version 1.0. When FMC executes, it will update the table version to 1.1 and add the additional data to the first 4 bytes of the reserved space at the end of the FHT.
manifest_load_addr
This is the physical address of the location in SRAM where ROM has placed a complete copy of the Firmware Manifest. This must remain resident such that firmware is able to re-run firmware integrity checks on-demand (required by FIPS 140-3).
fips_fw_load_addr_hdl
Future feature, not currently supported. This field provides the Handle of the DV entry that stores the physical address of the location in ROM or SRAM where a discrete FIPS Crypto module resides. If a discrete FIPS module does not exist, then this field shall be 0xFF and ROM, FMC, and RT FW must all carry their own code for accessing crypto resources and keys.
fmc_cdi_kv_hdl
This field provides the Handle into the Key Vault where the CDIFMC is stored.
fmc_priv_key_ecdsa_kv_hdl
This field provides the Handle into the Key Vault where the ECDSA PrivateKeyFMC is stored.
fmc_keypair_seed_mldsa_kv_hdl
This field provides the Handle into the Key Vault where the MLDSA Key Generation SeedFMC is stored.
fmc_pub_key_ecdsa_x_dv_hdl, fmc_pub_key_ecdsa_y_dv_hdl
These fields provide the Handle into the DCCM datavault where the ECDSA PublicKeyFMC X and Y coordinates are stored.
fmc_pub_key_mldsa_dv_hdl
This field provides the Handle into the DCCM datavault where the MLDSA PublicKeyFMC is stored.
fmc_cert_sig_ecdsa_r_dv_hdl, fmc_cert_sig_ecdsa_s_dv_hdl
These fields provide the Handle into the DCCM datavault where the ECDSA SignatureFMC R and S coordinates are stored.
fmc_cert_sig_mldsa_dv_hdl
This field provides the Handle into the DCCM datavault where the MLDSA SignatureFMC is stored.
rt_cdi_kv_hdl
This field provides the Handle into the Key Vault where the CDIRT is stored.
rt_priv_key_ecdsa_kv_hdl
This field provides the Handle into the Key Vault where the ECDSA PrivateKeyRT is stored.
rt_keygen_seed_mldsa_kv_hdl
This field provides the Handle into the Key Vault where the MLDSA Key Generation SeedRT is stored.
ldevid_tbs_ecdsa_addr
This field provides the address of the To Be Signed portion of the LDevID ECDSA certificate.
fmcalias_tbs_ecdsa_addr
This field provides the address of the To Be Signed portion of the FMC Alias ECDSA certificate.
ldevid_tbs_mldsa_addr
This field provides the address of the To Be Signed portion of the LDevID MLDSA certificate.
fmcalias_tbs_mldsa_addr
This field provides the address of the To Be Signed portion of the FMC Alias MLDSA certificate.
ldevid_tbs_ecdsa_size
This field provides the size of the To Be Signed portion of the LDevID ECDSA certificate.
fmcalias_tbs_ecdsa_size
This field provides the size of the To Be Signed portion of the FMC Alias ECDSA certificate.
ldevid_tbs_mldsa_size
This field provides the size of the To Be Signed portion of the LDevID MLDSA certificate.
fmcalias_tbs_mldsa_size
This field provides the size of the To Be Signed portion of the FMC Alias MLDSA certificate.
pcr_log_addr
Address in DCCM of the PCR log
pcr_log_index
Index within the PCR log of the next available log entry
meas_log_addr
Address in DCCM of the stashed measurement log
meas_log_index
Index within the measurement log of the next available log entry
fuse_log_addr
This field provides the address of the Fuse Log
rt_dice_pub_key_ecdsa
This field provides the Runtime Alias ECDSA Public Key.
rt_dice_pub_key_mldsa_dv_hdl
This field provides the Handle into the DCCM datavault where the Runtime Alias MLDSA Public Key is stored.
rt_dice_sign_ecdsa
This field provides the Runtime Alias certificate ECDSA signature.
rt_dice_sign_mldsa_dv_hdl
This field provides the Handle into the DCCM datavault where the Runtime Alias certificate MLDSA signature is stored.
ldevid_cert_sig_ecdsa_r_dv_hdl, ldevid_cert_sig_ecdsa_s_dv_hdl
These fields provide the Handle into the DCCM datavault where the ECDSA SignatureLDevId R and S coordinates are stored.
ldevid_cert_sig_mldsa_dv_hdl
This field provides the Handle into the DCCM datavault where the MLDSA SignatureLDevId is stored.
idev_dice_pub_key_ecdsa
This field provides the ECDSA IDevID Public Key.
idev_dice_pub_key_mldsa_dv_hdl
This field provides the Handle into the DCCM datavault where the MLDSA IDevID Public Key is stored.
rom_info_addr
This field provides the address of the RomInfo structure.
rtalias_tbs_ecdsa_size
This field provides the size of the To Be Signed portion of the Runtime Alias ECDSA certificate.
rtalias_tbs_mldsa_size
This field provides the size of the To Be Signed portion of the Runtime Alias MLDSA certificate.
fw_key_ladder_max_svn
This field informs firmware of the maximum FW SVN, which value was used to determine the length of FW's key ladder.
fw_key_ladder_kv_hdl
This field provides the Handle into the Key Vault where FW's key ladder is stored.
reserved
This area is reserved for definition of additional fields that may be added during Minor version updates of the FHT.
PCR registers
FMC has the responsibility to update 2 PCR registers.
FMC updates PCR3 to reflect the firmware update Journey with measurements of RT firmware and FW Manifest. This register is only cleared on cold reset.
FMC updates PCR2 to reflect only the Current running firmware with measurements of RT firmware and FW Manifest. This register is cleared on all reset types.
FMC locks its PCR registers before handing control to RT firmware so that they may not be cleared later in the boot.
FMC boot flow
The following list of steps are to be performed by FMC on each boot when ROM jumps to its entry point. Any failures are considered fatal.
- FMC locates the Firmware Handoff Table (FHT) responsible for passing vital configuration and other data from one firmware layer to the next. This is found at well-known address CALIPTRA_FHT_ADDR.
- FMC sanity checks FHT by verifying that fht.fht_marker == βCFHTβ and version is known/supported by FMC.
- FMC locates the discrete FW-based FIPS Crypto Module in ICCM using fht.fips_fw_base_addr (if not 0xFFFF_FFFF) and calls its initialization routine. Otherwise FMC utilizes the ROM-based FIPS Crypto Module or its own internal FIPS Crypto services in implementations without a discrete FW-based FIPS Crypto Module.
- FMC locates the Manifest at fht.manifest_load_addr.
- FMC reads the measurement of the Runtime FW Module, TCIRT, from the DCCM datavault that has previously been validated by ROM.
- FMC reads the manifest address of the Image Bundle from the HandOff Table, and calculates the SHA-384 TCIMAN
- FMC clears Current PCR
- FMC extends Current and Journey PCR registers with TCIRT.
- FMC extends Current and Journey PCR registers with TCIMAN.
- FMC locks Current and Journey PCR registers.
- FMC derives CDIRT from CDIFMC mixed with TCIRT and TCIMAN, then stores it in the Key Vault.
- FMC updates fht.rt_cdi_ecdsa_kv_hdl and fht.rt_cdi_mldsa_kv_hdl in the FHT.
- FMC derives ECDSA and MLDSA AliasKeyPairRT from CDIRT. The ECDSA Private Key and the MLDSA Key Generation Seed are stored in the Key Vault, while the ECDSA Public Key X and Y coordinates and MLDSA Public Key are stored in the DCCM datavault.
- FMC updates fht.rt_priv_key_ecdsa_kv_hdl, fht.rt_pub_key_ecdsa_x_dv_hdl, and fht.rt_pub_key_ecdsa_y_dv_hdl in the FHT.
- FMC updates fht.rt_keypair_seed_mldsa_kv_hdl and fht.rt_pub_key_mldsa_dv_hdl in the FHT.
- FMC generates ECDSA and MLDSA x509 certificates with ECDSA and MLDSA PubKeysRT as the subject and signed by PrivKeysFMC.
- FMC stores the ECDSA and MLDSA CertRT signatures in the DCCM datavault.
- FMC updates fht.rt_cert_sig_ecdsa_r_dv_hdl and fht.rt_cert_sig_ecdsa_s_dv_hdl in the FHT.
- FMC updates fht.rt_cert_sig_mldsa_dv_hdl in the FHT.
- FMC ensures that CDIFMC, ECDSA PrivateKeyFMC and MLDSA KeyPair Generation SeedFMC are locked to block further usage until the next boot.
- FMC locates the Runtime FW Module in ICCM at fht.rt_fw_load_addr.
- FMC jumps to the Runtime FW Module entry point at fht.rt_fw_entry_point.
Pre-conditions:
- Vault state as follows:
| Slot | Key Vault |
|---|---|
| 6 | Alias FMC CDI (64 bytes) |
| 7 | Alias FMC Private Key - ECDSA (48 bytes) |
| 8 | Alias FMC Key Pair Seed - MLDSA (32 bytes) |
| DCCM datavault |
|---|
| πLDevID ECDSA Pub Key X |
| πLDevID ECDSA Pub Key Y |
| πLDevID MLDSA Pub Key |
| πLDevID Cert ECDSA Signature R |
| πLDevID Cert ECDSA Signature S |
| πLDevID Cert MLDSA Signature |
| πAlias FMC ECDSA Pub Key X |
| πAlias FMC ECDSA Pub Key Y |
| πAlias FMC MLDSA Pub Key |
| πAlias FMC Cert Signature R |
| πAlias FMC Cert Signature S |
| πAlias FMC Cert MLDSA Signature |
| πFMC Digest |
| πFW SVN |
| πOwner PK Hash |
| πManufacturer Public Key Index |
*FMC Boot Sequence*
sequenceDiagram
participant ROM as Boot ROM
participant FIPS as FIPS Crypto
participant FMC as FMC
participant RT as Runtime FW
ROM->>+ROM: Early ROM Flow
ROM->>ROM: Authenticate FW Modules
ROM->>ROM: Enforce Anti-Rollback Protection
ROM->>ROM: Create fht
ROM->>-FMC: Jump to FMC Entry Point
FMC->>+FMC: SanityCheckFht(CALIPTRA_FHT_ADDR)
FMC->>FMC: LocateFipsFw(fht) (if needed)
FMC->>+FIPS: InitFipsFw() (if needed)
FIPS-->>-FMC: return()
FMC->>FMC: LocateManifest(fht)
FMC->>FMC: GetRtMeasurement(data_vault.rt_tci)
rect rgba(0, 100, 200, .2)
FMC->>+FIPS: ClearPcr(RtCurrent)
FIPS-->>-FMC: return()
FMC->>+FIPS: ExtendPcr(RtCurrent, PCR_hdl_RT, RtTci)
FIPS-->>-FMC: return()
FMC->>+FIPS: ExtendPcr(RtJourney, PCR_hdl_RT, RtTci)
FIPS-->>-FMC: return()
FMC->>+FIPS: LockPcr(RtCurrent)
FIPS-->>-FMC: return()
FMC->>+FIPS: LockPcr(RtJourney)
FIPS-->>-FMC: return()
end %% rect
rect rgba(0, 0, 200, .2)
note over FIPS, FMC: DICE-related derivations will be<br> defined in greater detail later
FMC->>+FIPS: DeriveCdi(fht.fmc_cdi_kv_hdl, "alias_rt_cdi", RtTci)
FIPS-->>-FMC: return(fht.rt_cdi_kv_hdl)
FMC->>+FIPS: DeriveKeyPair(fht.rt_cdi_kv_hdl, "alias_rt_ecc_key")
FIPS-->>-FMC: return(fht.rt_priv_key_ecdsa_kv_hdl,<br> fht.rt_pub_key_ecdsa_x_dv_hdl,<br> fht.rt_pub_key_ecdsa_y_dv_hdl)
FMC->>+FIPS: CertifyKey(fht.rt_pub_key_ecdsa_x_dv_hdl,<br> fht.rt_pub_key_ecdsa_y_dv_hdl,<br> fht.fmc_priv_key_ecdsa_kv_hdl)
FIPS-->>-FMC: return(fht.rt_cert_sig_ecdsa_r_dv_hdl, fht.rt_cert_sig_ecdsa_s_dv_hdl)
FMC->>+FIPS: DeriveKeyPair(fht.rt_cdi_kv_hdl, "alias_rt_mldsa_key")
FIPS-->>-FMC: return(fht.rt_mldsa_seed_kv_hdl,<br> fht.rt_pub_key_mldsa_dv_hdl)
FMC->>+FIPS: CertifyKey(fht.rt_pub_key_mldsa_dv_hdl,<br> fht.fmc_seed_mldsa_kv_hdl)
FIPS-->>-FMC: return(fht.rt_cert_sig_mldsa_dv_hdl)
FMC->>+FIPS: LockKey(fht.fmc_cdi_kv_hdl)
FIPS-->>-FMC: return()
FMC->>+FIPS: LockKey(fht.fmc_priv_key_ecdsa_kv_hdl)
FMC->>+FIPS: LockKey(fht.fmc_seed_mldsa_kv_hdl)
FIPS-->>-FMC: return()
end %% rect
FMC->>FMC: LocateRtFw(fht)
FMC->>-RT: Jump to Runtime Entry Point
activate RT
RT->>RT: RtFwInitFlow()
deactivate RT
Post-conditions:
- Vault state as follows:
| Slot | Key Vault |
|---|---|
| 4 | Alias RT CDI (64 bytes) |
| 5 | Alias RT Private Key (48 bytes) |
| 6 | Alias FMC CDI (64 bytes) |
| 7 | Alias FMC Private Key (48 bytes) |
| 8 | Alias FMC Key Pair Seed - MLDSA (32 bytes) |
| 9 | Alias RT Key Pair Seed - MLDSA (32 bytes) |
| DCCM datavault |
|---|
| πLDevID ECDSA Pub Key X |
| πLDevID ECDSA Pub Key Y |
| πLDevID MLDSA Pub Key |
| πLDevID Cert ECDA Signature R |
| πLDevID Cert ECDSA Signature S |
| πLDevID Cert MLDSA Signature |
| πAlias FMC ECDSA Pub Key X |
| πAlias FMC ECDSA Pub Key Y |
| πAlias FMC MLDSA Pub Key |
| πAlias FMC Cert ECDSA Signature R |
| πAlias FMC Cert ECDSA Signature S |
| πAlias FMC Cert MLDSA Signature |
| πFMC Digest |
| πFW SVN |
| πOwner PK Hash |
| πManufacturer Public Key Index |
Resets
FMC does not distinguish between cold boots or any other type of reset. Instead, FMC is designed such that it always performs the same set of operations regardless of which reset path caused it to be executed.
Update and recovery
FMC does not participate in Caliptra update/recovery flows. FMC is designed such that it does not perform any different steps during update and simply behaves the same as it does during other cold/warm resets.
Fake FMC
Fake FMC is a variation of the FMC intended to be used in the verification/enabling stages of development. The purpose is to greatly reduce the boot time for pre-Si environments by eliminating certain steps from the boot flow.
Differences from normal FMC: Currently, Fake FMC directly proceeds to runtime without generating the RT Alias Cert. In the future, there will be a static cert and a corresponding private key will be used by runtime to support the DICE challenge flow.
How to use:
- Fake FMC is provided in the release along with the normal collateral.
- The image builder exposes the argument "fake" that can be used to generate the fake versions
Fake FMC should be used with the Fake ROM. Details can be found in the ROM readme.
Future
- Current POR is for FIPS Crypto boundary to encompass all of Caliptra FW, including ROM, FMC, and Runtime. With this boundary, there is no need for any dedicated crypto module, and each layer of FW will include the library code it needs to access any required crypto functionality. In the future, if a more strict FIPS boundary is created, FMC will need to be changed to handle crypto operations differently. Depending on where it is implemented, it may or may not have to initilize the FIPS Crypto module, and it may need to use a different calling convention.
9248d79
Caliptra Runtime Firmware v2.0
Spec version: 0.3
This specification describes the Caliptra Runtime Firmware.
Changelog
v1.1:
v1.2:
v2.0:
- Add support for passive mode (same as 1.x) and subsystem (or active) mode
- MCU Runtime loading (subsystem mode)
- Cryptographic mailbox commands
ECDSA384_SIGNATURE_VERIFYandLMS_SIGNATURE_VERIFYrequire the hash to be included in the message, as the SHA accelerator registers are no longer accessible outside Caliptra.
v2.1:
Spec Opens
- Cryptographic Mailbox: ML-KEM support
Runtime Firmware environment
This section provides an overview of the Runtime Firmware environment.
Boot and initialization
The Runtime Firmware main function SHALL perform the following on cold boot reset:
- Initialize the DICE Protection Environment (DPE)
- Initialize any SRAM structures used by Runtime Firmware
- Upload the firwmare to the Manufacturer Control Unit (2.0, susbystem mode only)
For behavior during other types of reset, see Runtime firmware updates.
If Runtime Firmware detects that Caliptra was reset during the execution of an operation, Runtime Firmware calls DISABLE_ATTESTATION because the internal state of Caliptra may be corrupted.
Main loop
After booting, Caliptra Runtime Firmware is responsible for the following.
- Wait for mailbox interrupts. On mailbox interrupt, Runtime Firmware:
- Reads command from mailbox
- Executes command
- Writes response to mailbox and sets necessary status registers
- Sleeps until next interrupt
- On panic, Runtime Firmware:
- Saves diagnostic information
Callers must wait until Caliptra is no longer busy to call a mailbox command.
Fault handling
A mailbox command can fail to complete in the following ways:
- Hang or timeout, which result in the watchdog firing
- Unrecoverable panic
In both of these cases, the panic handler writes diagnostic panic information to registers that are readable by the SoC. Firmware then undergoes an impactless reset.
The caller is expected to check status registers upon reading responses from the mailbox.
Depending on the type of fault, the SoC may:
- Resubmit the mailbox command
- Attempt to update Runtime Firmware
- Perform a full SoC reset
- Some other SoC-specific behavior
Drivers
Caliptra Runtime Firmware will share driver code with ROM and FMC where possible; however, it will have its own copies of all of these drivers linked into the Runtime Firmware binary.
Cryptographic Mailbox Commands (new in 2.0)
Cryptographic mailbox (CM) commands are a flexible set of mailbox commands that provide access to Caliptra's cryptographic cabilities. This is meant for offloaded key storage and use, supporting protocols like SPDM and OCP LOCK.
These commands are not meant to be high-performance as they are accessed via mailbox commands.
CM itself does not provide any storage for the keys: when generated, they are returned to the caller in encrypted form, and must be passed back to be used.
These mailbox commands provide SHA, HMAC, HKDF, AES, RNG, MLDSA, and ECDSA services.
Note that while MLDSA and ECDSA keys can be imported, generated, and used in the cryptographic mailbox commands (i.e., CM_* commands) through CMKs, these keys are NOT tied DICE or DPE, so their use may be restricted for certain purposes.
MLDSA and ECDSA keys managed by DPE use the separate ECDSA384_SIGNATURE_VERIFY, LMS_SIGNATURE_VERIFY, and MLDSA87_SIGNATURE_VERIFY mailbox commands, which do not use the cryptographic mailbox system and are not managed by CMKs.
References
- SPDM 1.3.1 (DSP0274), dated 2024-07-01.
- OCP Attestation v1.1
- RFC 5869 (HKDF)
- RFC 8446 Section 7.4.2 & IEEE 1363 (TLS ECDH secret derivation)
Contexts
Several of the methods, such as SHA and AES, support contexts so that multiple users can have in-flight requests at the same time.
The contexts contain the internal structures necessary to resume operations to support data that may exceed the size of a single mailbox command.
These contexts are intended to be opaque to the user, and SHALL be encrypted and authenticated if they contain sensitive internal data.
Keys
Cryptographic Mailbox Key (CMKs) are used to store keys. Certain commands generate and return a new CMK. Most commands that use CMKs will also return a new CMK, as it is necessary to track CMKs so that they are not used beyond any relevant limits for their key type.
They are returned from commands that generate keys and must be passed back to Caliptra to be used. These keys are encrypted and opaque to the mailbox caller.
Internally, the unecrypted CMKs have the following structure:
| Name | Bits | Description |
|---|---|---|
| version | 16 | CMK version. Currently always 1. |
| length | 16 | how many bits of key material are used |
| key usage | 8 | represents which kind of key this is |
| id | 24 | ID number |
| usage counter | 64 | how many times this key has been used |
| This MAY only be tracked for AES keys | ||
| key material | 512 | bits used for the key material |
The encrypted CMKs have the structure:
| Name | Bits | Description |
|---|---|---|
| domain | 32 | reserved |
| domain metadata | 128 | reserved |
| iv | 96 | |
| ciphertext | 640 | encrypted CMK data (see above) |
| GCM tag | 128 |
The total size of the CMK is therefore 128 bytes.
Only the encrypted CMKs will appear in mailbox messages.
The key used to encrypt the CMKs is randomized on reset, which means that CMKs cannot be used between resets. The IV is a randomized 1-up counter that is incremented for every key created.
Key Usage
The internal CMK structure and several commands use a key usage tag to specify how a key can be used:
| Value | Usage |
|---|---|
| 0 | Reserved |
| 1 | HMAC |
| 2 | HKDF |
| 3 | AES |
Replay Prevention and Deletion
To prevent replay attacks, Caliptra will have a small table that maps a CMK's internal ID to its last known usage counters. Whenever a CMK is used, this table is checked and updated.
This is necessary for AES-256-GCM in particular to ensure that keys are only used a certain number of times, as per NIST SP 800-38D, Section 8.3. Only AES-256-GCM keys need to be tracked in this table, but other keys MAY be tracked as well.
This requires 96 bits of storage per AES-256-GCM key. These can stored as a sorted list in the DCCM.
Manifest-Based Image Authorization
Caliptra's goal is to enable integrators to meet standard security requirements for creating cryptographic identity and securely reporting measurements through DICE and DPE Certificate chains and Caliptra-owned private-public key pairs. In addition, Caliptra 1.0 provides an ECDSA384_SIGNATURE_VERIFY command to enable an SoC RoT to verify its own FW signatures so that it can develop an SoC secure boot using Caliptra cryptography. Caliptra 1.1 expanded the verify command to a PQC-safe LMS_SIGNATURE_VERIFY command. In each of these cases, it is left up to the vendor to ensure that they build a secure environment for introducing and verifying FW integrity and authenticity and then executing mutable FW.
The Caliptra Measurement manifest feature expands on Caliptra-provided secure verifier abilities. The Measurement Manifest feature provides a standard Caliptra-supported definition to enable the following use cases for integrators, vendors, and owners.
- Caliptra-Endorsed Aggregated Measured Boot
- Caliptra-Endorsed Local Verifier
Each of these abilities are tied to Caliptra Vendor and Owner FW signing keys and should be independent of any SoC RoT FW signing keys.
Manifest-based image authorization is implemented via two mailbox commands: SET_AUTH_MANIFEST, and AUTHORIZE_AND_STASH.
Caliptra-Endorsed Aggregated Measured Boot
Aggregated Measured Boot is a verified boot where one signed manifest attests to FW integrity of many different FW measurements. The authenticity of the FW is tied to the trust in the public key signing the measurement manifest, which is endorsed by the Caliptra Vendor and/or Owner FW Keys.
Caliptra-Endorsed Local Verifier
A local verifier provides an authentication of SoC FW by matching SoC FW measurements with measurements from the Caliptra measurement manifest. In this case, the SoC RoT still has its own FW public-key chain that is verified by the SoC RoT, but in addition the SoC RoT introduces the Caliptra Measurement Manifest, which is endorsed by the Caliptra FW key pair. Caliptra provides approval or disapproval of the measurement of any FW back to the SoC RoT. This effectively provides a multi-factor authentication of SoC FW.
The Caliptra-Endorsed Local Verifier could be required by the owner only or both the vendor and the owner.
The main difference between Caliptra-Endorsed Aggregated Measured Boot and Caliptra-Endorsed Local Verifier is if the SoC RoT is relying on the Measurement Manifest for SoC Secure Boot services as opposed as using it as an additional verification.
SoC RoT Enforcement of Measurement Manifest
In both use cases, the SoC RoT chooses to provide the Caliptra Measurement Manifest and to enforce the result of the authorization. Caliptra 1.x is not capable of providing any enforcement of measurements for SoC FW execution.
Caliptra Measurement Manifest Signing Keys Authenticity
Caliptra 1.0 and 1.1 do not put any requirements on how the SoC RoT ensures integrity and authenticity of SoC FW other than requiring the SoC RoT to provide a measurement to Caliptra of any SoC FW before execution. Caliptra Measurement Manifest enables the SoC RoT to perform the integrity check through Caliptra-authorized FW signing keys.
Unique Measurement Manifest Signing Keys
In order to reduce usage of the Caliptra FW Signing keys, the measurement manifest will be signed by new key pairs: one for the owner and possibly one for the vendor. These new key pairs are endorsed once using a single signature within the Measurement Manifest, thus allowing the measurement manifest keys to be used independently of the Caliptra FW signing keys.
Caliptra Measurement Manifest Vendor Public Key Authenticity
The Measurement Manifest MUST have an endorsement by the Caliptra Vendor Public Key. In order to fulfill this requirement, the Vendor has 2 options:
- Vendor signing required: The Vendor creates a new Measurement keypair which will sign the measurement manifest and endorses the new public key with the Caliptra FW Vendor Private Key. The signature covers both the new public key as well as the flags field which indicates that the new Measurement Key Pair will be enforced.
- Vendor signing not required: Vendor leaves the Vendor public key as all zeros, and clears the flag which enforces vendor signing and then endorses these fields with a signature in the Measurement Manifest. In this case, the Vendor releases ownership of enforcing any specific FW in execution.
Caliptra Measurement Manifest Owner Public Key Authenticity
Caliptra will always verify the endorsement of the Measurement Manifest Owner Public key and require that it signed the measurement manifest.
This feature is accomplished by having the SoC send a manifest to Caliptra Runtime through the SET_AUTH_MANIFEST mailbox command. The manifest will include a set of hashes for the different SoC images. Later, the SOC will ask for authorization for its images from the Caliptra Runtime through the AUTHORIZE_AND_STASH new mailbox commands. Caliptra Runtime will authorize the image based on whether its hash was contained in the manifest.
Preamble
The manifest begins with the Preamble section, which contains new manifest ECC and either MLDSA or LMS public keys of the vendor and the owner. These public keys correspond to the private keys that sign the Image Metadata Collection (IMC) section. These signatures are included in the Preamble. The Caliptra firmware's private keys endorse the manifest's public keys and these endorsements (i.e., signatures) are part of the Preamble as well.
Image Metadata Collection (IMC)
The IMC is a collection of Image Metadata entries (IME). Each IME has a hash that matches one of the multiple SoC images. The manifest vendor and owner private keys sign the IMC. The Preamble holds the IMC signatures. The manifest IMC vendor signatures are optional and are validated only if the Flags field Bit 0 is set to 1. Up to 127 image hashes will be supported.
Caliptra Measurement Manifest Keys Endorsement Verification Steps
When Caliptra receives the Measurement Manifest, Caliptra will:
- Verify the vendor endorsement using the Caliptra Vendor FW Public Key and compare with the vendor endorsement signature.
- If the vendor endorsement is invalid, the
SET_AUTH_MANIFESTcommand will be rejected. - If the vendor endorsement is valid, Caliptra will check if a vendor manifest measurement key is required:
- If the key is required, Caliptra will trust the Vendor Public key that was just endorsed.
- If the key is not required, Caliptra will not perform any more vendor verifications on this measurement manifest.
- Verify the owner endorsement using the Caliptra owner public key and compare with the owner endorsement signature.
- If the owner endorsement is invalid, the
SET_AUTH_MANIFESTcommand will be rejected. - Otherwise, the owner public key will be trusted and Caliptra will use it to verify the overall measurement manifest.
- If the owner endorsement is invalid, the
Measurement Manifest Version Number
A Measurement Manifest VN is used to ensure that some enforcement is possible if a progression of measurements is required. 32 bits of the existing unused IDEVID_MANUF_IDENTIFIER fuse (128 bits) can be repurposed for this. This can be accomplished by updating Caliptra's main specification to redefine the fuse definition and its usage from "Programming time" to "Field Programmable".
Image Authorization Sequence
The diagram below illustrates how this feature is part of the Caliptra boot flow, and the order of operations needed to use the feature.
sequenceDiagram
ROM->>FMC: Launch FMC
FMC->>Runtime: Launch RT
Runtime->>SOC: RDY_FOR_RT
Note over Runtime,SOC: Manifest Load
SOC->>Runtime: SET_MANIFEST
Runtime-->>SOC: Success/Failure
Note over Runtime,SOC: Image Authorization
loop n times
SOC->>Runtime: AUTHORIZE_AND_STASH
Runtime-->>SOC: Success/Failure
end
Note over Runtime,SOC: DPE Attestation
SOC->>Runtime: DPE Attestation
Mailbox commands
All mailbox command codes are little endian.
Table: Mailbox command result codes
| Name | Value | Description |
|---|---|---|
SUCCESS | 0x0000_0000 | Mailbox command succeeded |
BAD_VENDOR_SIG | 0x5653_4947 ("VSIG") | Vendor signature check failed |
BAD_OWNER_SIG | 0x4F53_4947 ("OSIG") | Owner signature check failed |
BAD_SIG | 0x4253_4947 ("BSIG") | Generic signature check failure (for crypto offload) |
BAD_IMAGE | 0x4249_4D47 ("BIMG") | Malformed input image |
BAD_CHKSUM | 0x4243_484B ("BCHK") | Checksum check failed on input arguments |
CME_BAD_CMK | 0x434D_424B ("CMBK") | Invalid CMK |
CME_CMK_OFLW | 0x434D_424F ("CMBO") | CMK has been used too many times |
CME_BAD_CTXT | 0x434D_4243 ("CMBC") | Bad context |
CME_FULL | 0x434D_4546 ("CMEF") | Too many Cryptographic Mailbox usage counters in use |
Relevant registers:
- mbox_csr -> COMMAND: Command code to execute.
- mbox_csr -> DLEN: Number of bytes written to mailbox.
- CPTRA_FW_ERROR_NON_FATAL: Status code of mailbox command. Any result
other than
SUCCESSsignifies a mailbox command failure.
Mailbox user 0xFFFF_FFFF is reserved for Caliptra internal use. All mailbox commands from that user will fail.
FW_LOAD
The FIRMWARE_LOAD command is handled by both ROM and Runtime Firmware.
ROM behavior
On cold boot, ROM exposes the FIRMWARE_LOAD mailbox command to accept
the firmware image that ROM will boot. This image includes Manifest, FMC, and Runtime
firmware.
Runtime Firmware behavior
Caliptra Runtime Firmware also exposes the FIRMWARE_LOAD mailbox command for loading
impactless updates. For more information, see Runtime Firmware updates.
Command Code: 0x4657_4C44 ("FWLD")
Table: FIRMWARE_LOAD input arguments
| Name | Type | Description |
|---|---|---|
| data | u8[...] | Firmware image to load. |
FIRMWARE_LOAD returns no output arguments.
FIRMWARE_VERIFY
The FIRMWARE_VERIFY command is used to verify a Caliptra Firmware Bundle.
Command Code: 0x4657_5652 ("FWVR")
Table: FIRMWARE_VERIFY input arguments
| Name | Type | Description |
|---|---|---|
| data | u8[...] | Firmware image bundle to verify. |
Table: FIRMWARE_VERIFY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| verify_result | u32 | VERIFY_SUCCESS (0xDEADC0DE), VERIFY_FAILED (0x21523F21) |
CAPABILITIES
Exposes a command to retrieve firmware capabilities
Command Code: 0x4341_5053 ("CAPS")
Table: CAPABILITIES input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: CAPABILITIES output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| capabilities | u8[16] | Firmware capabilities. See table below for details. |
Table: Firmware Capabilities Flags
| Name | Bit | Description |
|---|---|---|
RT_BASE | 64 | Base capabilities for Caliptra Runtime v2.1. |
RT_OCP_LOCK | 65 | Runtime firmware and hardware supports OCP LOCK. |
GET_IDEV_ECC384_CERT
Exposes a command to reconstruct the ECC384 IDEV CERT.
Command Code: 0x4944_4543 ("IDEC")
Table: GET_IDEV_ECC384_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| signature_r | u8[48] | R portion of signature of the cert. |
| signature_s | u8[48] | S portion of signature of the cert. |
| tbs_size | u32 | Size of the TBS. |
| tbs | u8[916] | TBS, with a maximum size of 916. Only bytes up to tbs_size are used. |
Table: GET_IDEV_ECC384_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| cert_size | u32 | Length in bytes of the cert field in use for the IDev ECC384 certificate. |
| cert | u8[1024] | DER-encoded IDev ECC384 CERT. |
GET_IDEV_MLDSA87_CERT
Exposes a command to reconstruct the MLDSA87 IDEV CERT.
Command Code: 0x4944_4D43 ("IDMC")
Table: GET_IDEV_MLDSA87_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| tbs_size | u32 | Size of the TBS. |
| signature | u8[4628] | MLDSA87 signature bytes. |
| tbs | u8[2820] | TBS, with a maximum size of 2820. Only bytes up to tbs_size are used. |
Table: GET_IDEV_MLDSA87_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| cert_size | u32 | Length in bytes of the cert field in use for the IDev MLDSA87 certificate. |
| cert | u8[...] | DER-encoded IDev MLDSA87 CERT. |
POPULATE_IDEV_ECC384_CERT
Exposes a command that allows the SoC to provide a DER-encoded ECC384 IDev certificate on every boot. The ECC384 IDev certificate is added to the start of the certificate chain.
Command Code: 0x4944_4550 ("IDEP")
Table: POPULATE_IDEV_ECC384_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| cert_size | u32 | Size of the DER-encoded ECC384 IDevId certificate. |
| cert | u8[1024] | DER-encoded ECC384 IDev CERT. |
Table: POPULATE_IDEV_ECC384_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
POPULATE_IDEV_MLDSA87_CERT
Exposes a command that allows the SoC to provide a DER-encoded MLDSA87 IDev certificate on every boot. The MLDSA87 IDev certificate is added to the start of the certificate chain.
Command Code: 0x4944_4D50 ("IDMP")
Table: POPULATE_IDEV_MLDSA87_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| cert_size | u32 | Size of the DER-encoded MLDSA87 IDev certificate. |
| cert | u8[8192] | DER-encoded MLDSA87 IDev CERT. |
Table: POPULATE_IDEV_MLDSA87_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
GET_IDEV_ECC384_INFO
Exposes a command to get the IDEVID ECC384 public key.
Command Code: 0x4944_4549 ("IDEI")
Table: GET_IDEV_ECC384_INFO input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_IDEV_ECC384_INFO output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| idev_pub_x | u8[48] | X portion of ECDSA IDevId key. |
| idev_pub_y | u8[48] | Y portion of ECDSA IDevId key. |
GET_IDEV_MLDSA87_INFO
Exposes a command to get the IDEVID MLDSA87 public key.
Command Code: 0x4944_4D49 ("IDMI")
Table: GET_IDEV_MLDSA87_INFO input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_IDEV_MLDSA87_INFO output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| idev_pub_key | u8[2592] | MLDSA IDevId public key. |
GET_LDEV_ECC384_CERT
Exposes a command to get an LDevID ECC384 certificate signed by ECC384 IDevID private key.
Command Code: 0x4C44_4556 ("LDEV")
Table: GET_LDEV_ECC384_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_LDEV_ECC384_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded ECC384 LDevID certificate. |
GET_LDEV_MLDSA87_CERT
Exposes a command to get an LDevID MLDSA87 certificate signed by MLDSA87 IDevID private key.
Command Code: 0x4C44_4D43 ("LDMC")
Table: GET_LDEV_MLDSA87_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_LDEV_MLDSA87_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded MLDSA87 LDevID certificate. |
GET_FMC_ALIAS_ECC384_CERT
Exposes a command to get a FMC alias ECC384 certificate signed by the ECC384 LDevID private key.
Command Code: 0x4345_5246 ("CERF")
Table: GET_FMC_ALIAS_ECC384_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_FMC_ALIAS_ECC384_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded FMC alias ECC384 certificate. |
GET_FMC_ALIAS_MLDSA87_CERT
Exposes a command to get a FMC alias MLDSA87 certificate signed by the MLDSA87 LDevID private key.
Command Code: 0x434D_4346 ("CMCF")
Table: GET_FMC_ALIAS_MLDSA87_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_FMC_ALIAS_MLDSA87_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded FMC alias MLDSA87 certificate. |
GET_RT_ALIAS_ECC384_CERT
Exposes a command to get a Runtime alias ECC384 certificate signed by the ECC384 FMC alias private key.
Command Code: 0x4345_5252 ("CERR")
Table: GET_RT_ALIAS_ECC384_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_RT_ALIAS_ECC384_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded Runtime alias ECC384 certificate. |
GET_RT_ALIAS_MLDSA87_CERT
Exposes a command to get a Runtime alias MLDSA87 certificate signed by the MLDSA87 FMC alias private key.
Command Code: 0x434D_4352 ("CMCR")
Table: GET_RT_ALIAS_MLDSA87_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_RT_ALIAS_MLDSA87_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded Runtime alias MLDSA87 certificate. |
ECDSA384_SIGNATURE_VERIFY
Verifies an ECDSA P-384 signature. The hash to be verified is taken from the input (new in 2.0).
In the event of an invalid signature, the mailbox command will report CMD_FAILURE and the cause will be logged as a non-fatal error.
Command Code: 0x4543_5632 ("ECV2")
Table: ECDSA384_SIGNATURE_VERIFY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| pub_key_x | u8[48] | X portion of ECDSA verification key. |
| pub_key_y | u8[48] | Y portion of ECDSA verification key. |
| signature_r | u8[48] | R portion of signature to verify. |
| signature_s | u8[48] | S portion of signature to verify. |
| hash | u8[48] | SHA384 digest to verify. |
Table: ECDSA384_SIGNATURE_VERIFY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
LMS_SIGNATURE_VERIFY
Verifies an LMS signature. The hash to be verified is taken from the input (new in 2.0).
In the event of an invalid signature, the mailbox command will report CMD_FAILURE and the cause will be logged as a non-fatal error.
The supported parameter set is limited to those used for the caliptra image signature: Table: LMS parameters
| Param Name | Value | Description |
|---|---|---|
| LMS algorithm type | 12 | 12 = LmsSha256N24H15 |
| LM-OTS algorithm type | 7 | 7 = LmotsSha256N24W4 |
| n | 24 | Bytes of output from sha256/192 hash function |
| w | 4 | Width (in bits) of the Winternitz coefficient |
| h | 15 | Height of the tree |
Command Code: 0x4C4D_5632 ("LMV2")
Table: LMS_SIGNATURE_VERIFY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| pub_key_tree_type | u8[4] | LMS public key algorithm type. Must equal 12. |
| pub_key_ots_type | u8[4] | LM-OTS algorithm type. Must equal 7. |
| pub_key_id | u8[16] | "I" Private key identifier |
| pub_key_digest | u8[24] | "T[1]" Public key hash value |
| signature_q | u8[4] | Leaf of the Merkle tree where the OTS public key appears |
| signature_ots | u8[1252] | LM-OTS signature |
| signature_tree_type | u8[4] | LMS signature Algorithm type. Must equal 12. |
| signature_tree_path | u8[360] | Path through the tree from the leaf associated with the LM-OTS signature to the root |
| hash | u8[48] | SHA384 digest to verify. |
Table: LMS_SIGNATURE_VERIFY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
MLDSA87_SIGNATURE_VERIFY
Verifies the signature against the message and MLDSA-87 public key.
The public key and signature formats are described in FIPS 204.
The command will only return a success if the signature is valid.
Command Code: 0x4D4C_5632 ("MLV2")
Table: MLDSA87_SIGNATURE_VERIFY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| pub_key | u8[2592] | Public key |
| signature | u8[4627] | Signature to check |
| padding | u8[1] | |
| data len | u32 | Length of message |
| data | u8[data len] | Message to check |
Table: MLDSA87_SIGNATURE_VERIFY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
INSTALL_OWNER_PK_HASH
Exposes a command to save the owner public key hash in persistent data.
Command Code: 0x4F57_4E50 ("OWNP")
Table: INSTALL_OWNER_PK_HASH input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| digest | u32[12] | Owner public key hash. |
Table: INSTALL_OWNER_PK_HASH output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| dpe_result | u32 | Result code, 0 on success. |
STASH_MEASUREMENT
Makes a measurement into the DPE default context. This command is intended for callers who update infrequently and cannot tolerate a changing DPE API surface.
- Call the DPE DeriveContext command with the DefaultContext in the locality of the PL0 PAUSER.
- Extend the measurement into PCR31 (
PCR_ID_STASH_MEASUREMENT). - Note: This command can only be called in the locality of the PL0 PAUSER.
Command Code: 0x4D45_4153 ("MEAS")
Table: STASH_MEASUREMENT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| metadata | u8[4] | 4-byte measurement identifier. |
| measurement | u8[48] | Data to measure into DPE. |
| context | u8[48] | Context field for svn; e.g., a hash of the public key that authenticated the SVN. |
| svn | u32 | SVN passed to the DPE to be used in the derived child. |
Table: STASH_MEASUREMENT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| dpe_result | u32 | Result code of DPE DeriveContext command. Little endian. |
DISABLE_ATTESTATION
Disables attestation by erasing the CDI and DICE key. This command is intended for callers who update infrequently and cannot tolerate a changing DPE API surface. It is intended for situations where Caliptra firmware cannot be loaded and the SoC must proceed with boot.
Upon receipt of this command, Caliptra's current CDI is replaced with zeroes, and the associated DICE key is re-derived from the zeroed CDI.
This command is intended to allow the SoC to continue booting for diagnostic and error reporting. All attestations produced in this mode are expected to fail certificate chain validation. Caliptra MUST undergo a cold reset in order to re-enable attestation.
Command Code: 0x4453_424C ("DSBL")
Table: DISABLE_ATTESTATION input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: DISABLE_ATTESTATION output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
INVOKE_DPE_ECC384
Invokes a serialized EC-P384 DPE profile command.
Command Code: 0x4450_4543 ("DPEC")
Table: INVOKE_DPE_ECC384 input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DPE command structure as defined in the DPE iRoT profile. |
Table: INVOKE_DPE_ECC384 output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DPE response structure as defined in the DPE iRoT profile. |
INVOKE_DPE_MLDSA87
Invokes a serialized ML-DSA-87 DPE profile command.
Command Code: 0x4450_4543 ("DPEC")
Table: INVOKE_DPE_MLDSA87 input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DPE command structure as defined in the DPE iRoT profile. |
Table: INVOKE_DPE_MLDSA87 output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DPE response structure as defined in the DPE iRoT profile. |
QUOTE_PCRS_ECC384
Generates a signed quote over all Caliptra hardware PCRs using the Caliptra PCR ECC384 quoting key. All PCR values are hashed together with the nonce to produce the quote.
Command Code: 0x5043_5251 ("PCRQ")
Table: QUOTE_PCRS_ECC384 input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| nonce | u8[32] | Caller-supplied nonce to be included in signed data. |
PcrValue is defined as u8[48]
Table: QUOTE_PCRS_ECC384 output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| PCRs | PcrValue[32] | Values of all PCRs. |
| nonce | u8[32] | Return the nonce used as input for convenience. |
| reset_ctrs | u32[32] | Reset counters for all PCRs. |
| digest | u8[48] | Return the lower 48 bytes of SHA2-512 digest over the PCR values and the nonce. |
| signature_r | u8[48] | ECC P-384 R portion of the signature over the ecc_digest. The FMC Alias ECC P-384 private key stored in Key Vault slot 7 is utilized for the signing operation. |
| signature_s | u8[48] | ECC P-384 S portion of the signature over the ecc_digest. |
QUOTE_PCRS_MLDSA87
Generates a signed quote over all Caliptra hardware PCRs that are using the Caliptra PCR Mldsa87 quoting key. All PCR values are hashed together with the nonce to produce the quote.
Command Code: 0x5043_524D ("PCRM")
Table: QUOTE_PCRS_MLDSA87 input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| nonce | u8[32] | Caller-supplied nonce to be included in signed data. |
PcrValue is defined as u8[48]
Table: QUOTE_PCRS_MLDSA87 output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| PCRs | PcrValue[32] | Values of all PCRs. |
| nonce | u8[32] | Return the nonce used as input for convenience. |
| reset_ctrs | u32[32] | Reset counters for all PCRs. |
| digest | u8[64] | Return the SHA2-512 digest over the PCR values and the nonce, in byte reversed order. |
| signature | u8[4628] | MLDSA-87 signature over the digest (4627 bytes + 1 Reserved byte). The FMC Alias MLDSA seed stored in Key Vault slot 8 is utilized to generate the private key, which is subsequently used for the signing operation. |
EXTEND_PCR
Extends a Caliptra hardware PCR.
Command Code: 0x5043_5245 ("PCRE")
Table: EXTEND_PCR input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| index | u32 | Index of the PCR to extend. |
| value | u8[..] | Value to extend into the PCR at index. |
Table: EXTEND_PCR output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
Note that extensions made into Caliptra's PCRs are not appended to Caliptra's internal PCR log.
GET_PCR_LOG
Gets Caliptra's internal PCR log.
Command Code: 0x504C_4F47 ("PLOG")
Table: GET_PCR_LOG input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_PCR_LOG output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | Internal PCR event log. |
See pcr_log.rs for the format of the log.
Note: the log contents reflect PCR extensions that are made autonomously by Caliptra during boot. The log contents are not preserved across cold or update resets. Callers who wish to verify PCRs that are autonomously extended during update reset should cache the log before triggering an update reset.
INCREMENT_PCR_RESET_COUNTER
Increments the reset counter for a PCR.
Command Code: 0x5043_5252 ("PCRR")
Table: INCREMENT_PCR_RESET_COUNTER input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| index | u32 | Index of the PCR for which to increment the reset counter. |
Table: INCREMENT_PCR_RESET_COUNTER output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
DPE_TAG_TCI
Associates a unique tag with a DPE context.
Command Code: 0x5451_4754 ("TAGT")
Table: DPE_TAG_TCI input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| handle | u8[16] | DPE context handle. |
| tag | u32 | A unique tag that the handle will be associated with. |
Table: DPE_TAG_TCI output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
DPE_GET_TAGGED_TCI
Retrieves the TCI measurements corresponding to the tagged DPE context.
Command Code: 0x4754_4744 ("GTGD")
Table: DPE_GET_TAGGED_TCI input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| tag | u32 | A unique tag corresponding to a DPE context. |
Table: DPE_GET_TAGGED_TCI output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| tci_cumulative | u8[48] | Hash of all of the input data provided to the context. |
| tci_current | u8[48] | Most recent measurement made into the context. |
FW_INFO
Retrieves information about the current Runtime Firmware, FMC, and ROM.
NOTE: Additional fields and info may be appended to the response in subsequent FW versions.
Command Code: 0x494E_464F ("INFO")
Table: FW_INFO input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: FW_INFO output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| pl0_pauser | u32 | PAUSER with PL0 privileges (from image header). |
| firmware_svn | u32 | Firmware SVN. |
| min_firmware_svn | u32 | Min Firmware SVN. |
| cold_boot_fw_svn | u32 | Cold-boot Firmware SVN. |
| attestation_disabled | u32 | State of attestation disable. |
| rom_revision | u8[20] | Revision (Git commit ID) of ROM build. |
| fmc_revision | u8[20] | Revision (Git commit ID) of FMC build. |
| runtime_revision | u8[20] | Revision (Git commit ID) of runtime build. |
| rom_sha256_digest | u32[8] | Digest of ROM binary. |
| fmc_sha384_digest | u32[12] | Digest of FMC binary. |
| runtime_sha384_digest | u32[12] | Digest of runtime binary. |
| owner_pub_key_hash | u32[12] | Hash of the owner public keys provided in the image bundle manifest. |
| authman_sha384_digest | u32[12] | Hash of the authorization manifest provided by SET_AUTH_MANIFEST. |
| most_recent_fw_error | u32 | Most recent FW non-fatal error (shows current non-fatal error if non-zero) |
VERSION
FIPS command to get version info for the module
Command Code: 0x4650_5652 ("FPVR")
Table: VERSION input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: VERSION output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error |
| mode | u32 | Mode identifier |
| fips_rev | u32[3] | [31:0] HW rev ID, [47:32] ROM version, [63:48] FMC version, [95:64] FW version |
| name | u8[12] | 12 character module name "Caliptra RTM" |
SELF_TEST_START
FIPS command to start the self tests
Command Code: 0x4650_4C54
Table: SELF_TEST_START input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: SELF_TEST_START output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error |
SELF_TEST_GET_RESULTS
FIPS command to get the results of the self tests. Mailbox command will return a failure if still active.
Command Code: 0x4650_4C67
Table: SELF_TEST_GET_RESULTS input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: SELF_TEST_GET_RESULTS output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error |
SHUTDOWN
FIPS command to zeroize and shut down the module
Command Code: 0x4650_5344 ("FPSD")
Table: SHUTDOWN input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: SHUTDOWN output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error |
ADD_SUBJECT_ALT_NAME
Provides a subject alternative name otherName. Whenever CERTIFY_KEY_EXTENDED is called with the DMTF_OTHER_NAME flag after ADD_SUBJECT_ALT_NAME is called, the resulting DPE CSR or leaf certificate will contain a subject alternative name extension containing the provided otherName, which must be a DMTF device info. All such certificates produced by CERTIFY_KEY_EXTENDED will continue to have the DMTF otherName subject alternative name extension until reset.
Command Code: 0x414C_544E ("ALTN")
Table: ADD_SUBJECT_ALT_NAME input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| dmtf_device_info_size | u32 | The size of the DMTF Device Info UTF8String. |
| dmtf_device_info | u8[128] | The DMTF Device Info UTF8String. |
Table: ADD_SUBJECT_ALT_NAME output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
CERTIFY_KEY_EXTENDED
Produces a DPE leaf certificate or CSR containing custom extensions provided by the SoC.
Command Code: 0x434B_4558 ("CKEX")
Table: CERTIFY_KEY_EXTENDED input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| certify_key_req | u8[72] | Certify Key Request. |
| flags | u32 | Flags determining which custom extensions to include in the certificate. |
Table: CERTIFY_KEY_EXTENDED input flags
| Name | Offset |
|---|---|
| DMTF_OTHER_NAME | 1 << 31 |
Table: CERTIFY_KEY_EXTENDED output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| certify_key_resp | u8[2176] | Certify Key Response. |
SET_AUTH_MANIFEST
The SoC uses this command and SET_IMAGE_METADTA to program an image manifest for Manifest-Based Image Authorization to Caliptra. In response to these commands, the Caliptra Runtime will verify the manifest by authenticating the public keys and in turn using them to authenticate the IMC. On successful verification, the Runtime will store the IMEs into DCCM for future use.
Command Code: 0x4154_4D4E ("ATMN")
Table: SET_AUTH_MANIFEST input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| manifest size | u32 | The size of the full Authentication Manifest |
| preamble_marker | u32 | Marker needs to be 0x4154_4D4E for the preamble to be valid |
| preamble_size | u32 | Size of the preamble |
| preamble_version | u32 | Version of the preamble |
| preamble_flags | u32 | Manifest flags. See AUTH_MANIFEST_FLAGS below |
| preamble_vendor_ecc384_key | u32[24] | Vendor ECC384 key with X and Y coordinates in that order |
| preamble_vendor_pqc_key | u32[648] | Vendor MLDSA-87 or LMS-SHA192-H15 key |
| preamble_vendor_ecc384_sig | u32[24] | Vendor ECC384 signature |
| preamble_vendor_PQC_sig | u32[1157] | Vendor MLDSA-87 or LMOTS-SHA192-W4 signature |
| preamble_owner_ecc384_key | u32[24] | Owner ECC384 key with X and Y coordinates in that order |
| preamble_owner_pqc_key | u32[648] | Owner MLDSA-87 or LMS-SHA192-H15 key |
| preamble_owner_ecc384_sig | u32[24] | Owner ECC384 signature |
| preamble_owner_PQC_sig | u32[1157] | Owner MLDSA-87 or LMOTS-SHA192-W4 signature |
| metadata_vendor_ecc384_sig | u32[24] | Metadata Vendor ECC384 signature |
| metadata_vendor_PQC_sig | u32[1157] | Metadata Vendor MLDSA-87 or LMOTS-SHA192-W4 signature |
| metadata_owner_ecc384_sig | u32[24] | Metadata Owner ECC384 signature |
| metadata_owner_PQC_sig | u32[1157] | Metadata Owner MLDSA-87 or LMOTS-SHA192-W4 signature |
| metadata_entry_entry_count | u32 | number of metadata entries |
| metadata_entries | Metadata[127] | The max number of metadata entries is 127 but less can be used |
Table: AUTH_MANIFEST_FLAGS input flags
| Name | Value |
|---|---|
| VENDOR_SIGNATURE_REQUIRED | 1 << 0 |
Table: AUTH_MANIFEST_METADATA_ENTRY digest entries
| Name | Type | Description |
|---|---|---|
| Image Hash | u8[48] | SHA2-384 hash of a SOC image. |
| Image_id | u32 | This corresponds to the Image Identifier field in the SoC Manifest
| Component_id | u32 | This corresponds to the Component Id field in the SoC Manifest
| flags | u32 | This corresponds to the flags field in the SoC Manifest
| Image Load Address High | u32 | This corresponds to the Image Load Address High field in the SoC Manifest
| Image Load Address Low | u32 | This corresponds to the Image Load Address Low field in the SoC Manifest
| Staging Address High | u32 | This corresponds to the Staging Address High field in the SoC Manifest
| Staging Address Low | u32 | This corresponds to the Staging Address Low field in the SoC Manifest
| Classification | u32 | This corresponds to the Classification field in the SoC Manifest
| Version Number | u32 | This corresponds to the Version Number field in the SoC Manifest
| Version String | u8[32] | This corresponds to the Version String field in the SoC Manifest
VERIFY_AUTH_MANIFEST
This command verifies the integrity and authenticity of the provided image manifest. Unlike SET_AUTH_MANIFEST, it performs validation only and does not persist the manifest in DCCM.
Command Code: 0x4154_564D ("ATVM")
The input arguments are the same as the SET_AUTH_MANIFEST command.
AUTHORIZE_AND_STASH
The SoC uses this command to request authorization of its various SoC images. This command has the option to receive the image hash directly from SoC or from an external source (e.g., SHA Acc).
The SoC uses this command repeatedly to ask for authorization to run its different images. The Runtime will verify that the image hash is contained in the IMC and will allow or reject the image based on that check. The command also enables stashing of the image hash by default with an option to skip stashing if needed. The SVN field is intended for anti-rollback protection.
Command Code: 0x4154_5348 ("ATSH")
Table: AUTHORIZE_AND_STASH input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| fw_id | u8[4] | Firmware id of the image, in little-endian format |
| measurement | u8[48] | Digest of the image requested for authorization. The source field needs to be set to '1` for InRequest, otherwisethis field is ignored. |
| context | u8[48] | Context field for svn; e.g., a hash of the public key that authenticated the SVN. |
| svn | u32 | The version of the image |
| flags | u32 | See AUTHORIZE_AND_STASH_FLAGS below |
| source | u32 | This field identifies the source of the digest to be used to compare with the SoC's SHA digest in the SoC Manifest Values 1 - InRequest - Use the hash in the 'measurement' field of this command 3 - LoadAddress - The image located in the ImageLoadAddress will be streamed to the SHA Accelerator to retrieve the digest that will be used for authorization. 4 - ImageStagingAddress - The image located in the StagingAddress will be streamed to the SHA Accelerator toretrieve the digest that will be used for authorization |
| image_size | u32 | The size of the image to hash. Only valid if source is ImageLoadAddress or StagingAddress |
Table: AUTHORIZE_AND_STASH_FLAGS input flags
| Name | Value |
|---|---|
| SKIP_STASH | 1 << 0 |
Table: AUTHORIZE_AND_STASH output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| auth_req_result | u32 |AUTHORIZE_IMAGE (0xDEADC0DE), IMAGE_NOT_AUTHORIZED (0x21523F21) or IMAGE_HASH_MISMATCH (0x8BFB95CB)
GET_IMAGE_INFO
The MCU uses this command to retrieve the Image Metadata Entry defined in the SoC Manifest given by an index to the Image Metadata Collection (IMC).
Command Code: 0x494D_4530 ("IME0")
Table: GET_IMAGE_INFO input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| fw_id | u32 | Firmware id of the image, in little-endian format
Table: GET_IMAGE_INFO output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| Component_id | u32 | This corresponds to the Component Id field in the SoC Manifest
| flags | u32 | This corresponds to the flags field in the SoC Manifest
| Image Load Address High | u32 | This corresponds to the Image Load Address High field in the SoC Manifest
| Image Load Address Low | u32 | This corresponds to the Image Load Address Low field in the SoC Manifest
| Staging Address High | u32 | This corresponds to the Staging Address High field in the SoC Manifest
| Staging Address Low | u32 | This corresponds to the Staging Address Low field in the SoC Manifest
| Classification | u32 | This corresponds to the Classification field in the SoC Manifest
| Version Number | u32 | This corresponds to the Version Number field in the SoC Manifest
| Version String | u8[32] | This corresponds to the Version String field in the SoC Manifest
ACTIVATE_FIRMWARE
The MCU uses this command to 'activate' the image that has been previously downloaded through PLDM - T5. For the full behavior of this command, refer to the Subsystem Support for Hitless Updates specification.
Command Code: 0x4143_5446 ("ACTF")
Table: ACTIVATE_FIRMWARE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| count | u32 | Number of image_ids to activate. Item count of image_ids array parameter |
| mcu_image_size | u32 | Size of MCU image, if included in the activation |
| image_ids | Array of u8[4] | Array of Image ids in little-endian format |
Table: ACTIVATE_FIRMWARE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
Mailbox commands: OCP LOCK v1.0
These commands are defined in the OCP LOCK v1.0 specification.
ENUMERATE_HPKE_HANDLES
REPORT_HEK_METADATA
GET_ALGORITHMS
INITIALIZE_MEK_SECRET
Mailbox commands: Cryptographic Mailbox (2.0)
These commands are used by the Cryptograhic Mailbox system.
CM_SHA_INIT
This starts the computation of a SHA hash of data, which may be larger than a single mailbox command allows. It also supports additional algorithms.
Note: ROM provides a simpler one-shot CM_SHA command for hashing data in a single operation. However, that command is ROM-only and is not available in runtime. For runtime, use these streaming commands (CM_SHA_INIT, CM_SHA_UPDATE, CM_SHA_FINAL) which support contexts and incremental hashing of large data.
The sequence to use these are:
- 1
CM_SHA_INITcommand - 0 or more
CM_SHA_UPDATEcommands - 1
CM_SHA_FINALcommand
For each command, the context from the previous command's output must be passed as an input.
The SHA_CONTEXT_SIZE is always exactly 200 bytes long.
The maximum supported data size for the SHA commands is 4096 bytes.
Command Code: 0x434D_5349 ("CMSI")
Table: CM_SHA_INIT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| hash algorithm | u32 | Enum. |
| Value 0 = reserved | ||
| Value 1 = SHA2-384 | ||
| Value 2 = SHA2-512 | ||
| data size | u32 | |
| data | u8[data size] | Data to hash |
Table: CM_SHA_INIT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | u8[SHA_CONTEXT_SIZE] | Passed to CM_SHA_UPDATE / CM_SHA_FINAL |
Table: CM_SHA_INIT / CM_SHA_UPDATE / CM_SHA_FINAL internal context
| Name | Type | Description |
|---|---|---|
| input buffer | u8[128] | |
| intermediate hash | u8[64] | |
| length | u32 | |
| hash algorithm | u32 |
CM_SHA_UPDATE
This continues a SHA computation started by CM_SHA_INIT or from another CM_SHA_UPDATE.
The context MUST be passed in from CM_SHA_INIT or CM_SHA_UPDATE.
Command Code: 0x434D_5355 ("CMSU")
Table: CM_SHA_UPDATE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| context | u8[SHA_CONTEXT_SIZE] | From CM_SHA_INIT / CM_SHA_UPDATE |
| data size | u32 | |
| data | u8[data size] | Data to hash |
Table: CM_SHA_UPDATE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | u8[SHA_CONTEXT_SIZE] | Passed to CM_SHA_UPDATE / CM_SHA_FINAL |
CM_SHA_FINAL
This finalizes the computation of a SHA and produces the hash of all of the data.
The context MUST be passed in from CM_SHA_INIT or CMA_SHA_UPDATE.
Command Code: 0x434D_5346 ("CMSF")
Table: CM_SHA_FINAL input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| context | u8[SHA_CONTEXT_SIZE] | From CM_SHA_INIT / CM_SHA_UPDATE |
| data size | u32 | May be 0 |
| data | u8[data size] | Data to hash |
Table: CM_SHA_FINAL output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| hash size | u32 | |
| hash | u8[hash size] |
CM_HMAC
Computes an HMAC according to RFC 2104 with select SHA algorithm support. The data must fit into a single mailbox command.
The CMK must have been created for HMAC / HKDF usage.
Command Code: 0x434D_484D ("CMHM")
Table: CM_HMAC input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | CMK to use as key |
| hash algorithm | u32 | Enum. |
| 0 = reserved | ||
| 1 = SHA2-384 | ||
| 2 = SHA2-512 | ||
| data size | u32 | |
| data | u8[data size] | Data to MAC |
Table: CM_HMAC output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| mac size | u32 | |
| mac | u8[mac size] |
CM_HMAC_KDF_COUNTER
Implements HMAC KDF in Counter Moder as specified in as specified in RFC 5869 and NIST SP800-108 Section 4.1 (KDF in Counter Mode, Section 4.1).
The CMK must have been created for HMAC usage.
The output length will be automatically chosen to match the key usage.
Command Code: 0x434D_4B43 ("CMKC")
Table: CM_HMAC_KDF_COUNTER input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| KIN CMK | CMK | Input key |
| hash algorithm | u32 | Enum. |
| Value 0 = reserved | ||
| Value 1 = SHA2-384 | ||
| Value 2 = SHA2-512 | ||
| key usage | u32 | usage tag of output key |
| key size | u32 | size (in bytes) for the output key; MUST be valid for the key usage |
| label size | u32 | |
| label | u8[label size] |
Table: CM_HMAC_KDF_COUNTER output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| KOUT CMK | CMK | CMK that stores the output key material |
CM_HKDF_EXTRACT
Implements HKDF-Extract as specified in RFC 5869.
The CMKs for IKM and salt must have been created for HMAC usage. The output will be tagged for HMAC usage.
Use CM_IMPORT to import non-secret (plaintext) salt or IKMs to use with HKDF-Extract after right-padding to 48 or 64 bytes with zeros.
Command Code: 0x434D_4B54 ("CMKT")
Table: CM_HKDF_EXTRACT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| hash algorithm | u32 | Enum. |
| Value 0 = reserved | ||
| Value 1 = SHA2-384 | ||
| Value 2 = SHA2-512 | ||
| salt CMK | CMK | Salt CMK. |
| IKM CMK | CMK | Input key material CMK |
Table: CM_HKDF_EXTRACT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| PRK CMK | u8[32] | CMK that stores the output (PRK) to use |
| with HKDF-Expand |
CM_HKDF_EXPAND
Implements HKDF-Expand as specified in RFC 5869.
The CMK must have been created for HMAC usage.
The output length will be automatically chosen to match the key usage.
Command Code: 0x434D_4B50 ("CMKP")
Table: CM_HKDF_EXPAND input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| PRK CMK | CMK | |
| hash algorithm | u32 | Enum. |
| Value 0 = reserved | ||
| Value 1 = SHA2-384 | ||
| Value 2 = SHA2-512 | ||
| key usage | u32 | usage tag of output key |
| key size | u32 | size (in bytes) for the OKM; |
| MUST be valid for the key usage | ||
| info size | u32 | |
| info | u8[info size] |
Table: CM_HKDF_EXPAND output arguments
Command Code: 0x434D_4B43 ("CMKC")
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| OKM CMK | CMK | CMK that stores the output key material |
CM_MLDSA_PUBLIC_KEY
Returns the public key associated with the MLDSA-87 key (seed) in a CMK.
The public key format is described in FIPS 204.
Command Code: 0x434D_4D50 ("CMMP")
Table: CM_MLDSA_PUBLIC_KEY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | Private key seed |
Table: CM_MLDSA_PUBLIC_KEY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| Public key | u8[2592] | Public key |
CM_MLDSA_SIGN
Signs the message with the MLDSA-87 key.
The signature format is described in FIPS 204.
Command Code: 0x434D_4D53 ("CMMS")
Table: CM_MLDSA_SIGN input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | Private key seed |
| data len | u32 | Length of message |
| data | u8[data len] | Message to sign |
Table: CM_MLDSA_SIGN output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| signature | u8[4627] | Signature |
| padding | u8[1] |
CM_MLDSA_VERIFY
Verifies the signature against the message and MLDSA-87 key.
The signature format is described in FIPS 204.
The command will only return a success if the signature is valid.
Command Code: 0x434D_4D56 ("CMMV")
Table: CM_MLDSA_VERIFY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | Private key seed |
| signature | u8[4627] | Signature to check |
| padding | u8[1] | |
| data len | u32 | Length of message |
| data | u8[data len] | Message to check |
Table: CM_MLDSA_VERIFY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
CM_ECDSA_PUBLIC_KEY
Returns the public key associated with the ECDSA-384 key seed in a CMK.
The public key consists of its x and y values described in FIPS 186-5 encoded in big-endian byte order.
Command Code: 0x434D_4550 ("CMEP")
Table: CM_ECDSA_PUBLIC_KEY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | Private key seed |
Table: CM_ECDSA_PUBLIC_KEY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| pubkey_x | u8[48] | The X BigNum of the ECDSA public key generated from the seed |
| pubkey_y | u8[48] | The Y BigNum of the ECDSA public key generated from the seed |
CM_ECDSA_SIGN
Signs the SHA384 hash of the message with the ECDSA-384 key.
The signature consists of its r and s values described in FIPS 186-5 encoded in big-endian byte order.
Command Code: 0x434D_5D53 ("CMES")
Table: CM_ECDSA_SIGN input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | Private key seed |
| data len | u32 | Length of message |
| data | u8[data len] | Message to sign |
Table: CM_ECDSA_SIGN output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| signature_r | u8[48] | The R BigNum of the ECDSA signature |
| signature_s | u8[48] | The S BigNum of the ECDSA signature |
CM_ECDSA_VERIFY
Verifies the signature against the SHA384 hash of the message and ECDSA-384 key.
The signature consists of its r and s values described in FIPS 186-5 encoded in big-endian byte order.
The command will only return a success if the signature is valid.
Command Code: 0x434D_4556 ("CMEV")
Table: CM_ECDSA_VERIFY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | Private key seed |
| signature_r | u8[48] | The R BigNum of an ECDSA signature |
| signature_s | u8[48] | The S BigNum of an ECDSA signature |
| data len | u32 | Length of message |
| data | u8[data len] | Message to check |
Table: CM_ECDSA_VERIFY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
CM_AES_ENCRYPT_INIT
Generic AES operation for unauthenticated AES operations. AES GCM operations use separate commands elsewhere.
AES-256-CBC only supports using a random 128-bit IV.
For CBC, the size must be a multiple of 16 bytes. CTR mode supports input of any size up to the maximum cryptographic mailbox size.
The CMK must have been created for AES usage.
Command Code: 0x434D_4349 ("CMCI")
Table: CM_AES_ENCRYPT_INIT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | CMK of the key to use to encrypt |
| mode/flags | u32 | Requested mode and flags. |
| 0 = Reserved | ||
| 1 = CBC | ||
| 2 = CTR | ||
| plaintext size | u32 | MUST be non-zero |
| plaintext | u8[plaintext size] | Data to encrypt |
Table: CM_AES_ENCRYPT_INIT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_CONTEXT | |
| iv | u8[16] | |
| ciphertext size | u32 | |
| ciphertext | u8[ciphertext size] | Output encrypted data |
The encrypted and authenticated context's internal structure will be:
Table: internal context for CM_AES_ operations*
| Name | Type | Description |
|---|---|---|
| mode | u32 | |
| key | u8[32] | |
| iv | u8[16] | |
| reserved | u8[76] | Reserved for additional fields |
The size of the (encrypted) context is always exactly 156 bytes,
and we will use the type AES_CONTEXT to represent u8[156].
CM_AES_ENCRYPT_UPDATE
This continues (or finishes) an AES computation started by CM_AES_ENCRYPT_INIT or from another CM_AES_ENCRYPT_UPDATE.
There is no CM_AES_ENCRYPT_FINISH since unauthenticated AES modes do not output a final tag.
The context MUST be passed in from CM_AES_ENCRYPT_INIT or CM_AES_ENCRYPT_UPDATE.
For CBC, the size must be a multiple of 16 bytes. CTR mode supports input of any size up to the maximum cryptographic mailbox size.
Command Code: 0x434D_4355 ("CMCU")
Table: CM_AES_ENCRYPT_UPDATE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| context | AES_CONTEXT | |
| plaintext size | u32 | MUST be non-zero |
| plaintext | u8[plaintext size] | Data to encrypt |
Table: CM_AES_ENCRYPT_UPDATE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_CONTEXT | |
| cipertext size | u32 | |
| ciphertext | u8[ciphertext size] |
CM_AES_DECRYPT_INIT
Starts an AES-256 unauthenaticed decryption computation.
The CMK must have been created for AES usage.
For CBC, the size must be a multiple of 16 bytes. CTR mode supports input of any size up to the maximum cryptographic mailbox size.
The IV must match what was passed and returned from the initial encryption operation.
Command Code: 0x434D_414A ("CMAJ")
Table: CM_AES_DECRYPT_INIT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | CMK to use for decryption |
| mode/flags | u32 | Requested mode and flags. |
| 0 = Reserved | ||
| 1 = CBC | ||
| 2 = CTR | ||
| iv | u8[16] | |
| ciphertext size | u32 | MUST be non-zero |
| ciphertext | u8[ciphertext size] | Data to decrypt |
Table: CM_AES_DECRYPT_INIT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_CONTEXT | |
| plaintext size | u32 | |
| plaintext | u8[plaintext size] | Decrypted data |
The encrypted and authenticated context's internal structure will be the same as for encryption.
CM_AES_DECRYPT_UPDATE
This continues an AES computation started by CM_AES_DECRYPT_INIT or from another CM_AES_DECRYPT_UPDATE.
There is no CM_AES_DECRYPT_FINISH since unauthenticated modes do not output a final tag.
The context MUST be passed in from CM_AES_DECRYPT_INIT or CM_AES_DECRYPT_UPDATE.
For CBC, the size must be a multiple of 16 bytes. CTR mode supports input of any size up to the maximum cryptographic mailbox size.
Command Code: 0x434D_4155 ("CMAU")
Table: CM_AES_DECRYPT_UPDATE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| context | AES_CONTEXT | |
| ciphertext size | u32 | MUST be non-zero |
| ciphertext | u8[ciphertext size] | Data to decrypt |
Table: CM_AES_DECRYPT_UPDATE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_CONTEXT | |
| plaintext size | u32 | |
| plaintext | u8[plaintext size] | Decrypted data |
CM_AES_GCM_ENCRYPT_INIT
Currently only supports AES-256-GCM with a random 96-bit IV.
Additional authenticated data (AAD) can only be passed during the INIT command, so is limited to the maximum cryptographic mailbox data size (4096 bytes).
The CMK must have been created for AES usage, except if the SPDM mode flag has been used, in which case the CMK must have been created for HMAC usage.
Command Code: 0x434D_4749 ("CMGI")
Table: CM_AES_GCM_ENCRYPT_INIT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| reserved | u32 | |
| CMK | CMK | CMK of the key to use to encrypt |
| aad size | u32 | |
| aad | u8[aad size] | Additional authenticated data |
Table: CM_AES_GCM_ENCRYPT_INIT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_GCM_CONTEXT | |
| iv | u8[12] |
The encrypted and authenticated context's internal structure will be:
Table: internal context for CM_AES_GCM_ENCRYPT_ operations*
| Name | Type | Description |
|---|---|---|
| key | u8[32] | |
| iv | u8[12] | |
| aad length | u32 | |
| GHASH state | u8[16] | |
| current length | u32 | value mod 16 is buffer size |
| buffer | u8[16] | |
| reserved | u8[16] |
The size of the (encrypted) context is always exactly 128 bytes,
and we will use the type AES_GCM_CONTEXT to represent u8[128] below.
CM_AES_GCM_SPDM_ENCRYPT_INIT
Derives the AES key and IV following the SPDM 1.4 and Secured Messages using SPDM 1.1 specifications.
Currently only supports AES-256-GCM.
Additional authenticated data (AAD) can only be passed during the INIT command, so is limited to the maximum cryptographic mailbox data size (4096 bytes).
The CMK must have been created for HMAC usage.
The CMK passed in should be the SPDM major secret CMK created for HMAC usage. The key and IV used for encryption shall follow the SPDM 1.4 section 12.7 derivation with key_length equal to 32 bytes and iv_length equal to 12 bytes.
EncryptionKey = HKDF-Expand(major-secret, bin_str5, key_length);
IV = HKDF-Expand(major-secret, bin_str6, iv_length);
bin_str5 = BinConcat(key_length, Version, "key", null);
bin_str6 = BinConcat(iv_length, Version, "iv", null);
The provided 64-bit message counter will be XOR'd with the IV to produce the message IV. The Secure SPDM standard requires the counter endianness flag to be little, but big endian is also supported to aid in compatibility with some implementations.
Note that it is critical that the same CMK and counter never be used more than once when encrypting or decrypting in SPDM mode as doing so could compromise the plaintext of the messages.
For the update and final operations, there are not any SPDM-specific commands; CM_AES_GCM_ENCRYPT_UPDATE and CM_AES_GCM_ENCRYPT_FINAL should be used.
Command Code: 0x434D_5345 ("CMSE")
Table: CM_AES_GCM_SPDM_ENCRYPT_INIT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| spdm version | u8 | The value should be equal to the |
| byte representation of the SPDM | ||
| version, e.g., 0x13 = SPDM 1.3 | ||
| counter endianness | u8 | XOR the counter with the IV using |
| the given endianness. | ||
| 0 = Little endian (standard) | ||
| 1 = Big endian | ||
| reserved | u8[2] | Reserved |
| counter | u8[8] | 8-byte counter in little-endian |
| format that is XOR'd into the IV | ||
| CMK | CMK | CMK of the key to use to encrypt |
| aad size | u32 | |
| aad | u8[aad size] | Additional authenticated data |
Table: CM_AES_GCM_SPDM_ENCRYPT_INIT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_GCM_CONTEXT |
Unlike in the standard AES GCM encrypt init command, the IV is not returned.
The encrypted and authenticated context's internal structure will be the same as for described in CM_AES_GCM_ENCRYPT_INIT.
CM_AES_GCM_ENCRYPT_UPDATE
This continues an AES computation started by CM_AES_GCM_ENCRYPT_INIT or from another CM_AES_GCM_ENCRYPT_UPDATE.
The context MUST be passed in from CM_AES_GCM_ENCRYPT_INIT or CM_AES_GCM_ENCRYPT_UPDATE.
Command Code: 0x434D_4755 ("CMGU")
Table: CM_AES_GCM_ENCRYPT_UPDATE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| context | AES_GCM_CONTEXT | |
| plaintext size | u32 | MUST be non-zero |
| plaintext | u8[plaintext size] | Data to encrypt |
Table: CM_AES_GCM_ENCRYPT_UPDATE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_GCM_CONTEXT | |
| cipertext size | u32 | could be greater than plaintext by 16 bytes |
| ciphertext | u8[ciphertext size] |
CM_AES_GCM_ENCRYPT_FINAL
This finalizes the computation of the AES GCM encryption and produces the final ciphertext and tag.
The context MUST be passed in from CM_AES_GCM_ENCRYPT_INIT or CM_AES_GCM_ENCRYPT_UPDATE.
Command Code: 0x434D_4746 ("CMGF")
Table: CM_AES_GCM_ENCRYPT_FINAL input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| context | AES_GCM_CONTEXT | |
| plaintext size | u32 | MAY be 0 |
| plaintext | u8[plaintext size] | Data to encrypt |
Table: CM_AES_GCM_ENCRYPT_FINAL output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| tag | u8[16] | |
| cipertext size | u32 | could be greater than plaintext by 16 bytes |
| ciphertext | u8[ciphertext size] |
The tag returned will always be 16 bytes. Shorter tags can be constructed by truncating.
CM_AES_GCM_DECRYPT_INIT
Starts an AES-256-GCM decryption computation.
Currently only supports AES-256-GCM with a 96-bit IV.
Additional authenticated data (AAD) can only be passed during the INIT command, so is limited to the maximum cryptographic mailbox data size (4096 bytes).
The AAD and IV must match what was passed and returned from the encryption operation.
The CMK must have been created for AES usage.
Command Code: 0x434D_4449 ("CMDI")
Table: CM_AES_GCM_DECRYPT_INIT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| reserved | u32 | Reserved |
| CMK | CMK | CMK to use for decryption |
| iv | u8[12] | |
| aad size | u32 | |
| aad | u8[aad size] | Additional authenticated data |
Table: CM_AES_GCM_DECRYPT_INIT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_GCM_CONTEXT |
The encrypted and authenticated context's internal structure will be the same as for encryption.
CM_AES_SPDM_GCM_DECRYPT_INIT
Starts an AES-256-GCM decryption computation in SPDM mode.
Currently only supports AES-256-GCM with a 96-bit IV.
Additional authenticated data (AAD) can only be passed during the INIT command, so is limited to the maximum cryptographic mailbox data size (4096 bytes).
The AAD must match what was passed and returned from the encryption operation.
The CMK must have been created for HMAC usage.
The CMK passed in should be the SPDM major secret CMK created for HMAC usage. The key and IV used for encryption shall follow the SPDM 1.4 section 12.7 derivation with key_length 256 and iv_length 96.
EncryptionKey = HKDF-Expand(major-secret, bin_str5, key_length);
IV = HKDF-Expand(major-secret, bin_str6, iv_length);
bin_str5 = BinConcat(key_length, Version, "key", null);
bin_str6 = BinConcat(iv_length, Version, "iv", null);
The provided 64-bit message counter will be XOR'd with the IV to produce the message IV. The Secure SPDM standard requires the counter endianness flag to be little, but big endian is also supported to aid in compatibility with some implementations.
Note that it is critical that the same CMK and counter never be used more than once when encrypting or decrypting in SPDM mode as doing so could compromise the plaintext of the messages.
Command Code: 0x434D_5344 ("CMSD")
Table: CM_AES_GCM_SPDM_DECRYPT_INIT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| spdm version | u8 | The value should be equal to the |
| byte representation of the SPDM | ||
| version, e.g., 0x13 = SPDM 1.3 | ||
| counter endianness | u8 | XOR the counter with the IV using |
| the given endianness. | ||
| 0 = Little endian (standard) | ||
| 1 = Big endian | ||
| reserved | u8[2] | Reserved |
| counter | u8[8] | 8-byte counter in little-endian |
| format that is XOR'd into the IV | ||
| CMK | CMK | CMK to use for decryption |
| aad size | u32 | |
| aad | u8[aad size] | Additional authenticated data |
Table: CM_AES_GCM_SPDM_DECRYPT_INIT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_GCM_CONTEXT |
The encrypted and authenticated context's internal structure will be the same as for encryption.
CM_AES_GCM_DECRYPT_UPDATE
This continues an AES computation started by CM_AES_GCM_DECRYPT_INIT or from another CM_AES_GCM_DECRYPT_UPDATE.
The context MUST be passed in from CM_AES_GCM_DECRYPT_INIT or CM_AES_GCM_DECRYPT_UPDATE.
Command Code: 0x434D_4455 ("CMDU")
Table: CM_AES_GCM_DECRYPT_UPDATE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| context | AES_GCM_CONTEXT | |
| ciphertext size | u32 | MUST be non-zero |
| ciphertext | u8[ciphertext size] | Data to decrypt |
Table: CM_AES_GCM_DECRYPT_UPDATE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | AES_GCM_CONTEXT | |
| plaintext size | u32 | MAY be 0 |
| plaintext | u8[plaintext size] |
CM_AES_GCM_DECRYPT_FINAL
This finalizes the computation of the AES GCM decryption and produces the final ciphertext.
The context MUST be passed in from CM_AES_GCM_DECRYPT_INIT or CM_AES_GCM_DECRYPT_UPDATE.
Tags between 0 and 16 bytes are supported but must be passed (on the right) with zeroes to 16 bytes.
The caller MUST verify that the tag verified field is set to 1 before using the result.
Command Code: 0x434D_4446 ("CMDF")
Table: CM_AES_GCM_DECRYPT_FINAL input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| context | AES_GCM_CONTEXT | |
| tag size | u32 | Can be 8, 9, ..., 16 |
| tag | u8[16] | Right-padded with zeroes |
| ciphertext size | u32 | MAY be 0 |
| ciphertext | u8[ciphertext size] | Data to decrypt |
Table: CM_AES_GCM_DECRYPT_FINAL output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| tag verified | u32 | 1 if tags matched, 0 if they did not |
| plaintext size | u32 | MAY be 0 |
| plaintext | u8[plaintext size] |
CM_ECDH_GENERATE
This computes the first half of an Elliptic Curve Diffie-Hellman exchange to compute an ephemeral shared key pair with another party.
Currently only supports the NIST P-384 curve.
The returned context must be passed to the CM_ECDH_FINISH command. The context contains the (encrypted) secret coefficient.
The returned exchange data format is the concatenation of the x- and y-coordinates of the public point encoded as big-endian integers, padded to 48 bytes each.
Command Code: 0x434D_4547 ("CMEG")
Table: CM_ECDH_GENERATE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 |
Table: CM_ECDH_GENERATE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| context | u8[76] | Used as the input to CM_ECDH_FINISH |
| exchange data | u8[96] | i.e., the public point |
Table: CM_ECDH_GENERATE / CM_ECDH_FINISH internal context
| Name | Type | Description |
|---|---|---|
| Secret coefficient | u8[48] |
The encrypted context size (76 bytes) is the size of the internal context (48 bytes) plus as 12-byte IV and a 16-byte authentication tag.
CM_ECDH_FINISH
This computes the second half of an Elliptic Curve Diffie-Hellman exchange.
Currently only supports the NIST P-384 curve.
The context must be passed from the CM_ECDH_GENERATE command.
The incoming exchange data MUST be the concatenation of the x- and y- coordinates of the other side's public point, encoded as big-endian integers, padded to 48 bytes each.
The produced shared secret is 384 bits.
Command Code: 0x434D_4546 ("CMEF")
Table: CM_ECDH_FINISH input arguments
| Name | Type | Description |
|---|---|---|
| chksum | ||
| context | u8[76] | This MUST come from the output of the CM_ECDH_GENERATE |
| key usage | u32 | usage tag of the kind of key that will be output |
| incoming exchange data | u8[96] | the other side's public point |
The context used as an input is the same as the output context from CM_ECDH_GENERATE above.
Table: CM_ECDH_FINISH output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| output CMK | CMK | Output CMK of the shared secret |
CM_RANDOM_STIR
This allows additional entropy to be added to the underlying deterministic random bit generator, if the hardware is using a CSRNG DRBG.
Command Code: 0x434D_5253 ("CMRS")
Table: CM_RANDOM_STIR input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| input size | u32 | |
| input | u8[input size] |
Table: CM_RANDOM_STIR output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
CM_RANDOM_GENERATE
This generates random bytes that are returned from the internal RNG.
Command Code: 0x434D_5247 ("CMRG")
Table: CM_RANDOM_GENERATE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| data size to return | u32 |
Table: CM_RANDOM_GENERATE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| output size | u32 | size of output |
| output | u8[output size] |
CM_DERIVE_STABLE_KEY
Derives an HMAC key that has a stable value across resets from either IDevId or LDevId.
The (interior) value of the returned CMK will be the stable across resets as it is derived indirectly from the IDevId or LDevId CDIs. The actual encrypted bytes of the CMK will not be the same, and the encrypted CMK itself cannot be used across resets. So, the key will always need to be re-derived after every cold reset.
If a key usage other than HMAC is desired, then the KDF or HKDF mailbox functions can be used to derive a key from the returned CMK.
Command Code: 0x434D_4453 ("CMDS")
Table: CM_DERIVE_STABLE_KEY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| key_type | u32 | Source key to derive the stable key from. 0x0000_0001: IDevId 0x0000_0002: LDevId |
| info | u8[32] | Data to use in the key derivation. |
Table: CM_DERIVE_STABLE_KEY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| cmk | CMK | CMK that stores the stable key material |
CM_IMPORT
Imports the specified key and returns a CMK for it.
Usage information is required so that the key can be verified and used appropriately.
Command Code: 0x434D_494D ("CMIM")
Table: CM_IMPORT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| key usage | u32 | Tag to specify how the data can be used |
| input size | u32 | This MUST agree with the key usage |
| input | u8[input size] |
Table: CM_IMPORT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| CMK | CMK | CMK containing imported key |
CM_DELETE
Deletes the object stored with the given mailbox ID.
Command Code: 0x434D_444C ("CMDL")
Table: CM_DELETE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| CMK | CMK | CMK to delete |
Table: CM_DELETE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
CM_CLEAR
The entire contents of the CMK storage is wiped. All known keys will be invalidated.
Command Code: 0x434D_434C ("CMCL")
CM_CLEAR takes no input arguments.
Table: CM_CLEAR output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
CM_STATUS
Queries the status cryptographic mailbox system.
The usage storage records the number of entries used and total for for usage tracking. Usage tracking is only currently implemented for AES, so this is is effectively the number of AES keys used and total potential available AES keys.
Command Code: 0x434D_5354 ("CMST")
CM_STATUS takes no input arguments.
Table: CM_STATUS output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| used usage storage | u32 | CMK usage storage (in entries) |
| total usage storage | u32 | Total CMK usage storage (in entries) |
GET_IDEV_ECC384_CSR
Command Code: 0x4944_4352 ("IDCR")
Table: GET_IDEV_ECC384_CSR input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_IDEV_ECC384_CSR output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded ECC384 IDevID certificate signing request. |
GET_IDEV_MLDSA87_CSR
Command Code: 0x4944_4d52 ("IDMR")
Table: GET_IDEV_MLDSA87_CSR input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_IDEV_MLDSA87_CSR output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded MLDSA87 IDevID certificate signing request. |
The mfg_flag_gen_idev_id_csr manufacturing flag MUST have been set to generate a CSR.
When called from ROM, if the CSR was not previously provisioned this command will return FW_PROC_MAILBOX_UNPROVISIONED_CSR(0x0102000A).
When called from runtime, if the CSR was not previously provisioned this command will return RUNTIME_GET_IDEV_ID_UNPROVISIONED(0x000E0051). If the ROM did not support CSR generation, this command will return RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM(0x000E0052).
When the mfg_flag_gen_idev_id_csr flag has been set, the SoC MUST wait for the flow_status_set_idevid_csr_ready bit to be set by Caliptra. Once set, the SoC MUST clear the mfg_flag_gen_idev_id_csr flag for Caliptra to progress.
GET_FMC_ALIAS_ECC384_CSR
Command Code: 0x464D_4352 ("FMCR")
Table: GET_FMC_ALIAS_ECC384_CSR input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_FMC_ALIAS_ECC384_CSR output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded ECC384 FMC Alias certificate signing request. |
GET_FMC_ALIAS_MLDSA87_CSR
Command Code: 0x464d_4452 ("FMDR")
Table: GET_FMC_ALIAS_MLDSA87_CSR input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
Table: GET_FMC_ALIAS_MLDSA87_CSR output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[...] | DER-encoded MLDSA87 FMC Alias certificate signing request. |
SIGN_WITH_EXPORTED_ECDSA
Command Code: 0x5357_4545 ("SWEE")
Note: This command is only available in the locality of the PL0 PAUSER.
Table: SIGN_WITH_EXPORTED_ECDSA input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| exported_cdi_handle | u8[32] | The Exported CDI handle returned by the DPE DeriveContext command. Little endian. |
| tbs | u8[48] | The bytes to be signed. Little endian. |
Table: SIGN_WITH_EXPORTED_ECDSA output arguments
| Name | Type | Description |
|---|---|---|
| derived_pubkey_x | u8[48] | The X BigNum of the ECDSA public key associated with the signing key. |
| derived_pubkey_y | u8[48] | The Y BigNum of the ECDSA public key associated with the signing key. |
| signature_r | u8[48] | The R BigNum of an ECDSA signature. |
| signature_s | u8[48] | The S BigNum of an ECDSA signature. |
The exported_cdi can be created by calling DeriveContext with the export-cdi and create-certificate flags.
REVOKE_EXPORTED_CDI_HANDLE
Command Code: 5256_4348 ("RVCH")
Note: This command is only available in the locality of the PL0 PAUSER.
Table: REVOKE_EXPORTED_CDI_HANDLE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| exported_cdi_handle | u8[32] | The Exported CDI handle returned by the DPE DeriveContext command. Little endian. |
The exported_cdi can be created by calling DeriveContext with the export-cdi and create-certificate flags.
The exported_cdi_handle is no longer usable after calling REVOKE_EXPORTED_CDI_HANDLE with it. After the exported_cdi_handle
has been revoked, a new exported CDI can be created by calling DeriveContext with the export-cdi and create-certificate flags.
EXTERNAL_MAILBOX_CMD
Command Code: 0x4558_544D ("EXTM")
Note: This command is only available in subsystem mode in 2.1+.
Executes a mailbox command located at an AXI address. This allows for executing mailbox commands that are larger than the mailbox allows.
This is currently mostly useful for FIRMWARE_LOAD (as part of an update) or SET_AUTH_MANIFEST.
The response is still written to the mailbox.
The checksum is over the EXTM command, not the command that is loaded over AXI. That external command will still need its own checksum, if applicable.
Table: EXTERNAL_MAILBOX_CMD input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| command_id | u32 | Command ID for the mailbox command to be executed. Little endian. |
| command_size | u32 | Size of the mailbox command to be executed. Little endian. |
| axi_address_low | u32 | Lower 32 bits of the AXI address that contains the mailbox command. Little endian. |
| axi_address_high | u32 | High 32 bits of the AXI address that contains the mailbox command. Little endian. |
The response will be the response of the executed external command.
REALLOCATE_DPE_CONTEXT_LIMITS
Command Code: '5243_5458` ("RCTX")
Note: This command is only available in the locality of the PL0 PAUSER.
Table: REALLOCATE_DPE_CONTEXT_LIMITS input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| pl0_context_limit | u32 | Number of contexts to allocate to PL0. PL1 will receive remaining contexts. |
Table: REALLOCATE_DPE_CONTEXT_LIMITS output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| new_pl0_context_limit | u32 | Number of contexts assigned to PL0 after the reallocation |
| new_pl1_context_limit | u32 | Number of contexts assigned to PL1 after the reallocation |
This allows the user to reallocate the 32 DPE contexts between PL0 and PL1. By default, each gets 16 contexts.
Note: 2 PL0 contexts are used by Caliptra itself during initialization.
Checksum
For every command except for FIRMWARE_LOAD, the request and response feature a checksum. This mitigates glitches between clients and Caliptra.
The checksum is a little-endian 32-bit value, defined as:
0 - (SUM(command code bytes) + SUM(request/response bytes))
The sum of all bytes in a request/response body, and command code, should be zero.
If Caliptra detects an invalid checksum in input parameters, it returns
BAD_CHKSUM as the result.
Caliptra also computes a checksum over all of the responses and writes it to the chksum field.
FIPS status
For every command, the firmware responds with a FIPS status of FIPS approved. There is currently no use case for any other responses or error values.
Table: FIPS status codes
| Name | Value | Description |
|---|---|---|
FIPS_APPROVED | 0x0000_0000 | Status of command is FIPS approved |
RESERVED | 0x0000_0001 - 0xFFFF_FFFF | Other values reserved, will not be sent by Caliptra |
Runtime Firmware updates
Caliptra Runtime Firmware accepts impactless updates that update Caliptraβs firmware without resetting other cores in the SoC.
Applying updates
A Runtime Firmware update is triggered by the FIRMWARE_LOAD command. Upon
receiving this command, Runtime Firmware does the following:
- Locks the mailbox to writes
- Invokes impactless reset
After impactless reset is invoked, FMC loads the hash of the image from the verified Manifest into the necessary PCRs:
- Runtime Journey PCR
- Runtime Latest PCR
If ROM validation of the image fails:
- ROM SHALL NOT clear the Runtime Latest PCR. It SHALL still re-lock this PCR with the existing value.
- FMC SHALL NOT extend either of the Runtime PCRs.
Boot process after update
After an impactless update is applied, the new Runtime Firmware is able to sample a register to determine if it has undergone an Impactless Reset. In this case, the new Runtime Firmware must:
- Validate DPE state in SRAM
- Ensure the TCI tree is well-formed
- Ensure all nodes chain to the root (TYPE = RTMR, βInternal TCIβ flag is set)
- Verify that the βLatest TCIβ field of the TCI Node that contains the
Runtime PCRs (TYPE = RTMR, βInternal TCIβ flag is set) matches the
βLatestβ and Journey Runtime PCR values.
- Ensure
SHA384_HASH(0x00..00, TCI from SRAM) == RT_FW_JOURNEY_PCR
- Ensure
- Check that retired and inactive contexts do not have tags
- If any validations fail, Runtime Firmware executes the
DISABLE_ATTESTATIONcommand
DICE Protection Environment (DPE)
Caliptra Runtime Firmware SHALL implement a profile of the DICE Protection Environment (DPE) API.
PAUSER privilege levels
Caliptra uses PAUSER as a HW mechanism to distinguish DPE Client localities. Caliptra models PAUSER callers to its mailbox as having 1 of 2 privilege levels:
- PL0 - High privilege. Only 1 PAUSER in the SoC may be at PL0. The PL0 PAUSER is denoted in the signed Caliptra firmware image. The PL0 PAUSER may call any supported DPE commands. Only PL0 can use the CertifyKey command. Success of the CertifyKey command signifies to the caller that it is at PL0. Only PL0 can use the POPULATE_IDEV_ECC384_CERT and POPULATE_IDEV_MLDSA87_CERT mailbox commands.
- PL1 - Restricted privilege. All other PAUSERs in the SoC are PL1. Caliptra SHALL fail any calls to the DPE CertifyKey with format=X509 by PL1 callers. PL1 callers should use the CSR format instead.
PAUSER and Locality map 1:1. Consequently, only the single DPE Client associated with PL0 level, is authorized to invoke CertifyKey DPE command with format=x509. All other DPE Clients have instead restricted privileges associated to PL1 (as described above).
PAUSER privilege level active context limits
Each active context in DPE is activated from either PL0 or PL1 through the InvokeDpe mailbox command calling the DeriveContext or InitializeContext DPE commands. However, a caller could easily exhaust space in DPE's context array by repeatedly calling the aforementioned DPE commands with certain flags set.
To prevent this, we establish active context limits for each PAUSER privilege level:
- PL0 - 16 active contexts
- PL1 - 16 active contexts
If a DPE command were to activate a new context such that the total number of active contexts in a privilege level is above its active context limit, the InvokeDpe command should fail.
At boot Caliptra Runtime FW consumes part of the PL0 active contexts (initially 16) to DeriveContext for:
- RTFW Journey (RTFJ) Measurement (1)
- Mailbox Valid Pauser digest (MBVP) (1)
- ROM Stashed Measurements (max 8)
Further, it is not allowed for PL1 to call DeriveContext with the intent to change locality to PL0's locality; this would increase the number of active contexts in PL0's locality, and hence allow PL1 to DOS PL0.
DPE profile implementation
The DPE iRoT profile leaves some choices up to implementers. This section describes specific requirements for the Caliptra DPE implementation.
| Name | Value | Description |
|---|---|---|
| Profile Variant | DPE_PROFILE_IROT_P384_SHA384 | The profile variant that Caliptra implements. |
| KDF | SP800-108 HMAC-CTR | KDF to use for CDI (tcg.derive.kdf-sha384) and asymmetric key (tcg.derive.kdf-sha384-p384) derivation. |
| Simulation Context Support | Yes | Whether Caliptra implements the optional Simulation Contexts feature. |
| Supports ExtendTci | Yes | Whether Caliptra implements the optional ExtendTci command. |
| Supports Auto Init | Yes | Whether Caliptra will automatically initialize the default DPE context. |
| Supports Rotate Context | Yes | Whether Caliptra supports the optional RotateContextHandle command. |
| CertifyKey Alias Key | Caliptra Runtime Alias Key | The key that will be used to sign certificates that are produced by the DPE CertifyKey command. |
Supported DPE commands
Caliptra DPE supports the following commands:
- GetProfile
- InitializeContext
- DeriveContext
- Note: The "export-cdi" flag is only available in the locality of the PL0 PAUSER.
- CertifyKey
- Caliptra DPE supports two formats for CertifyKey: X.509 and PKCS#10 CSR. X.509 is only available to PL0 PAUSERs.
- Sign
- RotateContextHandle
- DestroyContext
- GetCertificateChain
DPE state atomicity
This implementation guarantees that no internal DPE state is changed if a command fails for any reason. This includes context handle rotation; single-use context handles are not rotated if a command fails.
On failure, DPE only returns a command header, with no additional command-specific response parameters. This is in line with the CBOR-based main DPE spec, which does not return a response payload on failure.
Initializing DPE
Caliptra Runtime Firmware is responsible for initializing DPEβs default context.
- Runtime Firmware SHALL initialize the default context in βinternal-cdiβ mode.
- Perform the following initial measurements:
- Call DeriveContext with Caliptra RT PCRs
- INPUT_DATA = PCRX (RT journey PCR as defined in the FHT)
- TYPE = βRTMRβ
- CONTEXT_HANDLE = default context
- TARGET_LOCALITY = Caliptra locality (0xFFFFFFFF)
- Call DeriveContext with mailbox valid PAUSERS
- INPUT_DATA = Hash of CPTRA_VALID_PAUSER register.
- TYPE = βMBVPβ
- CONTEXT_HANDLE = default context
- TARGET_LOCALITY = PL0 PAUSER
- Call DeriveContext for each STASH_MEASUREMENT call made during Caliptra ROM execution
- INPUT_DATA =
measurementparameter to STASH_MEASUREMENT - TYPE =
typeparameter to STASH_MEASUREMENT - CONTEXT_HANDLE = default context
- TARGET_LOCALITY = PL0 PAUSER
- INPUT_DATA =
- Call DeriveContext with Caliptra RT PCRs
CDI derivation
The DPE Sign and CertifyKey commands derive an asymmetric key for that handle.
DPE first collects measurements and concatenates them in a byte buffer,
MEASUREMENT_DATA:
- LABEL parameter passed to Sign or CertifyKey.
- The
TCI_NODE_DATAstructures in the path from the current TCI node to the root, inclusive, starting with the current node.
To derive a CDI for a given context, DPE shall use KeyVault hardware with the following inputs:
- CDI = Runtime Firmware CDI (from KeyVault)
- Label = LABEL parameter provided to Sign or CertifyKey
- Context =
MEASUREMENT_DATA
The CDI shall be loaded into KeyVault slot 8.
Leaf key derivation
To derive an asymmetric key for Sign and CertifyKey, Runtime Firmware does the following:
- Derives an ECC P384 keypair from KV slot 8 CDI into KV slot 9
- For CertifyKey: Requests the public key
- For Sign: Signs passed data
- Erases KeyVault slots 8 and 9
Internal representation of TCI nodes
| Byte offset | Bits | Name | Description |
|---|---|---|---|
| 0x00 | 383:0 | TCI_CURRENT | Current TCI measurement value |
| 0x30 | 383:0 | TCI_CUMULATIVE | TCI measurement value |
| 0x60 | 31:0 | TYPE | TYPE parameter to the DeriveContext call that created this node |
| 0x64 | 31:0 | LOCALITY | TARGET_LOCALITY parameter to the DeriveContext call that created this node (PAUSER) |
Certificate generation
The DPE Runtime Alias Key SHALL sign DPE leaf certificates and CSRs.
The DPE GET_CERTIFICATE_CHAIN command shall return the following certificates:
- IDevID (optionally added by the SoC via POPULATE_IDEV_ECC384_CERT)
- LDevID
- FMC Alias
- Runtime Alias
DPE leaf certificate definition
| Field | Sub field | Value |
|---|---|---|
| Version | v3 | 2 |
| Serial Number | First 20 bytes of sha256 hash of DPE Alias public key | |
| Issuer Name | CN | Caliptra Runtime Alias |
| serialNumber | First 20 bytes of sha384 hash of Runtime Alias public key | |
| Validity | notBefore | notBefore from firmware manifest |
| notAfter | notAfter from firmware manifest | |
| Subject Name | CN | Caliptra DPE Leaf |
| serialNumber | SHA384 hash of Subject public key | |
| Subject Public Key Info | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Public Key | DPE Alias Public Key value | |
| Signature Algorithm Identifier | Algorithm | ecdsa-with-SHA384 |
| Parameters | Named Curve = prime384v1 | |
| Signature Value | Digital signature for the certificate | |
| KeyUsage | keyCertSign | 1 |
| Basic Constraints | CA | False |
| Policy OIDs | id-tcg-kp-attestLoc | |
| tcg-dice-MultiTcbInfo* | FWIDs | [0] "Current" TCI Value. Latest INPUT_DATA made by DeriveContext |
| IntegrityRegisters | [0] "Journey" TCI Value. | |
| Type | 4-byte TYPE field of TCI node | |
| VendorInfo | Locality of the caller (analog for PAUSER) |
*MultiTcbInfo contains one TcbInfo for each TCI Node in the path from the current TCI Node to the root. Max of 32.
a6784b6
Caliptra Manufacturer Control Unit (MCU) Firmware and SDK
Spec revision: 0.9
The Caliptra MCU firmware is be provided as a reference software development kit (SDK) with a consistent foundation for building a quantum-resilient and standards-compliant Root of Trust (RoT) for SoC implementers. It extends the Caliptra core system to provide the Caliptra Subsystem set of services to the encompassing system.
While Caliptra Core provides support for Identity, Secure Boot, Measured Boot, and Attestation, the Caliptra MCU firmware will be responsible for enabling Recovery, RoT Services, and Platform integration support. All SoC RoTs have specific initialization sequences and scenarios that need to be supported beyond standard RoT features. Hence, the MCU firmware will be distributed as Rust SDK with batteries included to build RoT Applications.
The Caliptra MCU SDK is composed of two major parts:
ROM: When the MCU first boots, it executes a small, bare metal ROM. The ROM is responsible for sending non-secret fuses to Caliptra core so that it can complete booting. The MCU ROM will be silicon-integrator-specific. However, the MCU SDK will provide a ROM framework that binds the MCU and Caliptra. The ROM will also be used with the software emulator, RTL Simulator, and FPGA stacks. For more details, see the ROM specification.
Runtime: The majority of the MCU firmware SDK is the runtime firmware, which provides the majority of the services after booting. Most of the documentation here consists of the documentation for the runtime.
Principles
Caliptra 2.x firmware aspires to be the foundation for the RoT used in SoCs integrating Caliptra. Hence architecture, design and implementation must abide by certain guiding principles. Many of these principles are the founding principles for the Caliptra Project.
The MCU firmware SDK will follow the same principles as the Caliptra core's firmware.
-
Open and Extensible: The MCU SDK is published as open source and available via GitHub, and uses a build toolchain for repeatable builds. The MCU SDK provided is a reference implementation, and is intended to be extended and adaptable by integrators.
-
Consistent: Vendors require features like Identity, Secure Boot, Measured Boot, Attestation, Recovery, Update, Anti-Rollback, and ownership transfer. There are standards defined by various organizations around these feature areas. The MCU SDK will follow TCG, DMTF, OCP, PCIe and other standards to guarantee consistency on RoT features and behavior.
-
Compliant: The MCU SDK must be compliant with security standards and audits. Security audits will be performed and audit reports will be published on GitHub. In addition, the SDK will also be audited for OCP SAFE and short form reports will be published to GitHub.
-
Secure and Safe Code: The MCU SDK will follow the standards in Caliptra Core for security and memory safety as first principle and will be implemented in Rust.
SDK Features
The following sections give a short overview of the features and goals of the MCU SDK.
Development
Software emulators for Caliptra, MCU, and a reference SoC will be provided as part of the MCU SDK. Software emulators will be an integrated part of CI/CD pipelines. In addition, Caliptra also has RTL Simulators based on Verilator that can be used for regression testing. The Caliptra hardware team also has reference hardware running on FPGA platforms.
RTOS
MCU provides various simultaneous, complex RoT services. Hence an RTOS is needed. As the MCU SDK is implemented in Rust, we need Rust-based RTOS. MCU will leverage the Tock Embedded Operating System, an RTOS designed for secure embedded systems. MCU uses the Tock Kernel while providing a Caliptra async user mode API surface area. Tock provides a good security and isolation model to build an ROT stack.
Drivers
Tock has a clean user and kernel mode separation. Components that directly access hardware and provide abstractions to user mode are implemented as Tock drivers and capsules (kernel modules). The MCU SDK will provide the following drivers:
- UART Driver
- I3C Driver
- Fuse Controller Driver
- Caliptra Mailbox Driver
- MCI Mailbox Driver
- SPI Flash Driver (may be replaced by a silicon-specific flash driver)
Silicon integrators may provide their own drivers to implement SoC-specific features that are not provided by the MCU SDK. For example, if an SoC implements TEE-IO, the silicon vendor will be responsible for providing PCIe IDE and TDISP drivers.
Stacks
As the Caliptra RoT is built on the foundation of industry standard protocols, the MCU SDK will provide the following stacks:
- MCTP
- PLDM
- PLDM Firmware Update
- OCP Streaming Boot
- SPDM based Attestation
- PCIe IDE
- TDISP
- SPI Flash Boot
APIs and Services
Caliptra RoT will provide common foundational services and APIs required to be implemented by all RoT. The following are the APIs and services provided by MCU SDK:
- Image Loading
- Attestation
- Signing Key management and revocation
- Anti-Rollback protection
- Firmware Update
- Life Cycle Management
- Secure Debug Unlock
- Ownership Transfer
- Cryptographic API
- Certificate Store
- Key-Value Store
- Logging and Tracing API
Applications
Each SoC has unique features and may need a subset of security services. Hence silicon integrators will be responsible for authoring ROT applications leveraging MCU SDK. MCU SDK will ship with a set of applications that will demonstrate the features of the MCU SDK.
Tooling and Documentation
The MCU SDK will feature
- Repeatable build environments to build RoT Applications
- Image generation, verification, and signing tools
- Libraries for interacting with Caliptra ROT to perform various functions like attestation, firmware updates, log retrieval, etc.
- An MCU SDK Architecture and Design Specifications
- An MCU SDK Developers' Guide
- An MCU SDK Integrators' Guide
- The MCU SDK API
a6784b6
Reference ROM Specification
The reference ROM is executed when the MCU starts.
The ROM's main responsibilities to the overall Caliptra subsystem are to:
- Send non-secret fuses to Caliptra core
- Initialize I3C and the firmware recovery interface
- Jump to firmware
It can also handle any other custom SoC-specific initialization that needs to happen early.
Boot Flows
There are three main boot flows that needs to execute for its role in the Caliptra subsystem:
- Cold Boot Flow
- Firmware Update Flow
- Warm Reset Flow
These are selected based on the MCI RESET_REASON register that is set by hardware whenver the MCU is reset.
Cold Boot Flow
- Check the MCI
RESET_REASONregister for MCU status (it should be in cold boot mode) - Initialize I3C recovery interface. For AXI bypass boot, only the recovery interface initialization is required; basic I3C initialization can be skipped.
- For I3C boot: Initialize I3C registers according to the initialization sequence, then initialize I3C recovery interface per the recovery flow.
- For AXI bypass boot: Only initialize the recovery interface registers needed for streaming boot.
- Assert Caliptra boot go signal to bring Caliptra out of reset.
- Read Caliptra SoC
FLOW_STATUSregister to wait for Caliptra Ready for Fuses state. - Anything SoC-specific can happen here
- Read non-secret fuses from the OTP controller. The authoritative fuse map is contained in the main Caliptra specification.
- Write fuse data to Caliptra SoC interface fuse registers. The following fuses are written to the corresponding Caliptra registers:
FUSE_PQC_KEY_TYPE: Vendor PQC key type (2 bits)FUSE_FMC_KEY_MANIFEST_SVN: FMC key manifest SVN (32 bits)FUSE_VENDOR_PK_HASH: Vendor public key hash (384 bits)FUSE_RUNTIME_SVN: Runtime SVN (128 bits)FUSE_SOC_MANIFEST_SVN: SoC manifest SVN (128 bits)FUSE_SOC_MANIFEST_MAX_SVN: SoC manifest max SVN (32 bits)FUSE_MANUF_DBG_UNLOCK_TOKEN: Manufacturing debug unlock token (128 bits)FUSE_ECC_REVOCATION: Vendor ECC key revocation (4 bits)FUSE_LMS_REVOCATION: Vendor LMS key revocation (32 bits)FUSE_MLDSA_REVOCATION: Vendor MLDSA key revocation (4 bits)CPTRA_OWNER_PK_HASH: Owner public key hash (384 bits, written only if non-zero, could be overridden by device ownership transfer)FUSE_SOC_STEPPING_ID: SoC stepping ID (16 bits)FUSE_ANTI_ROLLBACK_DISABLE: Anti-rollback disable (1 bit)FUSE_IDEVID_CERT_ATTR: IDevID certificate attributes (768 bits)FUSE_IDEVID_MANUF_HSM_ID: IDevID manufacturing HSM identifier (128 bits)SS_UDS_SEED_BASE_ADDR_L/H: UDS/FE partition base address in OTPSS_STRAP_GENERIC: OTP DAI idle bit offset and direct access command register offset- [MCI]
PROD_DEBUG_UNLOCK_PK_HASH_REGProduction debug unlock public key hashes (384 bytes total for 8 key hashes)
- Configure MCU mailbox AXI users (see Security Configuration below).
- Set mailbox AXI user lock registers.
- [2.1] Set FC_FIPS_ZEROZATION to the appropriate value.
- Set
SS_CONFIG_DONE_STICKY,SS_CONFIG_DONEregisters to lock MCI configuration. - Verify PK hashes and MCU mailbox AXI users after locking (see Security Configuration below).
- Poll on Caliptra
FLOW_STATUSregisters for Caliptra to deassert the Ready for Fuses state. - Handle device ownership transfer, if applicable.
- Send the
RI_DOWNLOAD_FIRMWAREcommand to Caliptra to start the firmware loading process. Caliptra will:- Follow all of the steps in the Caliptra ROM documentation for firmware loading in the ROM cold reset.
- Transition to Caliptra runtime firmware.
- Load the SoC manifest over the recovery interface and verify it.
- Load the MCU runtime over the recovery interface registers to the MCU SRAM.
- Verify the MCU runtime against the SoC manifest.
- [2.1] If the MCU runtime is encrypted:
- Caliptra runtime returns to mailbox processing mode.
- MCU derives or imports the decryption key to the Caliptra cryptographic mailbox.
- MCU issues a CM_AES_GCM_DECRYPT_DMA command to decrypt the firmware in MCU SRAM.
- MCU issues the ACTIVATE_FIRMWARE command to Caliptra to activate the MCU firmware.
- Caliptra sets the MCI
FW_EXEC_CTRL[2]bit to indicate that MCU firmware is ready
- Wait for Caliptra to indicate MCU firmware is ready by polling the firmware ready status.
- Stash the MCU ROM and other security-sensitive measurements to Caliptra. (In 2.1 subsystem mode, this should happen after Caliptra runtime is available using CM_SHA_{INIT,UPDATE,FINAL}. In 2.0 or 2.1 core mode, this could potentially happen earlier using the CM_SHA ROM command.)
- MCU ROM triggers a reset by writing
0x1to the MCIRESET_REQUESTregister. This generates a hardware reset of the MCU core while maintaining power. The MCI hardware automatically setsRESET_REASONtoFirmwareBootReset, causing the MCU to restart and enter the Firmware Boot Reset flow, which will jump to the loaded firmware.
sequenceDiagram
note right of mcu: check reset reason
mcu->>mci: assert Caliptra boot go
note right of mcu: initialize recovery interface
loop wait for ready for fuses
mcu->>caliptra: read flow status
end
note right of mcu: SoC-specific init
mcu->>otp: read non-secret fuses
otp->>mcu: non-secret fuses
mcu->>caliptra: set non-secret fuses
mcu->>mci: configure MCU mailbox AXI users
mcu->>mci: lock MCU mailbox AXI users
mcu->>mci: set SS_CONFIG_DONE_STICKY
mcu->>mci: set SS_CONFIG_DONE
mcu->>mci: verify PK hashes
mcu->>mci: verify MCU mailbox AXI users
loop wait for NOT ready for fuses
mcu->>caliptra: read flow status
end
mcu->>caliptra: RI_DOWNLOAD_FIRMWARE command
note right of caliptra: authenticate FW header/TOC
note right of caliptra: verify signatures
note right of caliptra: load/decrypt Caliptra FW
note right of caliptra: derive runtime keys
alt encrypted MCU FW
note right of caliptra: decrypt and load MCU FW to SRAM
else unencrypted MCU FW
note right of caliptra: load MCU FW to SRAM
end
caliptra->>mci: set FW_EXEC_CTL[2]
loop wait for firmware ready
mcu->>mci: check FW_EXEC_CTL
end
mcu->>mci: write RESET_REQUEST (0x1)
note right of mcu: resets to Firmware Boot flow
Firmware Boot Flow
This flow is used to boot the MCU into the MCU Runtime Firmware following either a cold or warm reset. It ensures that the runtime firmware is properly loaded and ready for execution.
- Check the MCI
RESET_REASONregister for MCU status (it should be in firmware boot reset modeFirmwareBootReset) - Set flow checkpoint to indicate firmware boot flow has started
- Validate that firmware was actually loaded by checking the firmware entry point is not zero
- Set flow milestone to indicate firmware boot flow completion
- Jump directly to runtime firmware at the configured SRAM offset
sequenceDiagram
note right of mcu: check reset reason (FirmwareBootReset)
note right of mcu: set flow checkpoint
note right of mcu: validate firmware at entry point
alt firmware valid
note right of mcu: set completion milestone
note right of mcu: jump to runtime firmware
else firmware invalid
note right of mcu: fatal error - halt
end
Hitless Firmware Update Flow
Hitless Update Flow is triggered when MCU runtime FW requests an update of the MCU FW by sending the ACTIVATE_FIRMWARE mailbox command to Caliptra. Upon receiving the mailbox command, Caliptra will initialize the MCU reset sequence causing the MCU to boot to ROM and run the Hitless Firmware Update Flow.
- Check the MCI
RESET_REASONregister for reset status (it should be in hitless firmware update modeFirmwareHitlessUpdate). - Enable the
notif_cptra_mcu_reset_req_stsinterrupt. - Check if firmware is already available by reading the interrupt status
- Clear
notif_cptra_mcu_reset_req_stsinterrupt status - If firmware is available:
- Wait for Caliptra to clear FW_EXEC_CTRL[2]. This will be indicated when
notif_cptra_mcu_reset_req_stsinterrupt status bit is set - Clear the
notif_cptra_mcu_reset_req_stsinterrupt. This triggers Caliptra to copy MCU FW from the staging area to MCU SRAM.
- Wait for Caliptra to clear FW_EXEC_CTRL[2]. This will be indicated when
- Wait for Caliptra to set FW_EXEC_CTRL[2].
- Release Caliptra mailbox. Hitless Update is triggered by a mailbox command from MCU to Caliptra which causes it to reboot to ROM, therefore the mailbox needs to be released after the update is complete.
- Jump to runtime firmware at the configured SRAM offset
sequenceDiagram
note right of mcu: check reset reason (FirmwareHitlessUpdate)
note right of mcu: enable reset request interrupt
mcu->>mci: check if firmware already available
mcu->>mci: clear reset request interrupt status
alt firmware already available
loop wait for Caliptra to clear FW_EXEC_CTRL[2]
mcu->>mci: check reset request status
end
mcu->>mci: clear reset request interrupt (triggers FW copy)
end
loop wait for Caliptra to set FW_EXEC_CTRL[2]
mcu->>caliptra: check fw_ready status
end
mcu->>caliptra: release mailbox (finish response)
loop verify firmware ready
mcu->>caliptra: check fw_ready status
end
note right of mcu: jump to runtime firmware
Warm Reset Flow
Warm Reset Flow occurs when the subsystem reset is toggled while powergood is maintained high. This is allowed when MCU and Caliptra already loaded their respective mutable firmware, prior to the warm reset. MCU and Caliptra FW will not be reloaded in this flow.
- Check the MCI
RESET_REASONregister for reset status (it should be in warm reset modeWarmReset) - Assert Caliptra boot go signal to bring Caliptra out of reset.
- Wait for Caliptra to be ready for fuses (even though fuses won't be rewritten)
- Set
SS_CONFIG_DONEregister to lock MCI configuration until next warm reset. - Signal fuse write done to Caliptra to complete the fuse handshake protocol
- Wait for Caliptra to deassert ready for fuses state
- Wait for Caliptra to indicate that MCU firmware is ready in SRAM
- Validate that firmware was actually loaded by checking the firmware entry point is not zero
- Set flow checkpoint and milestone to indicate warm reset flow completion
- Trigger a warm reset to transition to
FirmwareBootResetflow which will jump to the firmware
sequenceDiagram
note right of mcu: check reset reason (WarmReset)
note right of mcu: set flow checkpoint
mcu->>caliptra: assert boot go signal
loop wait for ready for fuses
mcu->>caliptra: check ready for fuses status
end
mcu->>mci: set SS_CONFIG_DONE
mcu->>mci: set SS_CONFIG_DONE
mcu->>caliptra: signal fuse write done
loop wait for NOT ready for fuses
mcu->>caliptra: check ready for fuses status
end
loop wait for firmware ready
mcu->>caliptra: check firmware ready status
end
note right of mcu: validate firmware at entry point
note right of mcu: set completion milestone
mcu->>mci: trigger warm reset (to FirmwareBootReset)
Failures
On any fatal or non-fatal failure, MCU ROM can use the MCI registers FW_ERROR_FATAL and FW_ERROR_NON_FATAL to assert the appropriate errors.
In addition, SoC-specific failure handling may occur.
There will also be a watchdog timer running to ensure that the MCU is reset if not the ROM flow is not progressing properly.
Security Configuration
The MCU ROM is responsible for configuring and locking security-sensitive MCI registers during the boot process. This section describes the security requirements and verification steps that must be performed.
Configuration Locking
The MCI provides two configuration lock registers:
SS_CONFIG_DONE_STICKY: Locks configuration registers until the next cold reset. This is set during cold boot after all sticky configuration is complete.SS_CONFIG_DONE: Locks configuration registers until the next warm reset. This is set during both cold boot and warm boot flows.
These registers must be set after configuring security-sensitive registers and before signaling fuse write done to Caliptra.
Security Requirement: After setting these registers, MCU ROM must verify that they are actually set by reading them back. If either register fails to set, the ROM must trigger a fatal error (ROM_SOC_SS_CONFIG_DONE_VERIFY_FAILED) and halt. This ensures that the locking mechanism itself has not been tampered with.
Production Debug Unlock PK Hash Verification
The PROD_DEBUG_UNLOCK_PK_HASH_REG registers in MCI store the public key hashes used for production debug unlock authentication. These registers are writable by the MCU but have no access protections before SS_CONFIG_DONE_STICKY is set. This creates a window where another AXI user could potentially manipulate the PK hashes between configuration and locking.
Security Requirement: MCU ROM must verify that the PK hash values written to MCI match the expected values from the fuse controller after both SS_CONFIG_DONE_STICKY and SS_CONFIG_DONE are set. If verification fails, the ROM must trigger a fatal error and halt.
The verification process:
- MCU ROM writes PK hash values from fuses to
PROD_DEBUG_UNLOCK_PK_HASH_REGregisters - [2.1] MCU ROM writes the FC_FIPS_ZEROZATION register
- MCU ROM sets
SS_CONFIG_DONE_STICKYandSS_CONFIG_DONEto lock the registers - MCU ROM reads back the PK hash register values and compares them against the original fuse values
- [2.1] MCU ROM reads back the FC_FIPS_ZEROIZATION register and verifies its value.
- If any mismatch is detected, MCU ROM reports a fatal error (
ROM_SOC_PK_HASH_VERIFY_FAILED)
MCU Mailbox AXI User Verification
The MBOX0_VALID_AXI_USER and MBOX1_VALID_AXI_USER registers control which AXI users can access the MCU mailboxes. These registers are locked using MBOX0_AXI_USER_LOCK and MBOX1_AXI_USER_LOCK, which make the corresponding AXI user registers read-only. Note that unlike the PK hash registers, the MBOX registers are not affected by SS_CONFIG_DONE*βonly the LOCK registers control their access.
Security Requirement: MCU ROM must configure the MCU mailbox AXI users, lock them using the LOCK registers, and verify that both the AXI user values and the lock register values match the expected configuration. If verification fails, the ROM must trigger a fatal error and halt. The verification is performed after SS_CONFIG_DONE_STICKY and SS_CONFIG_DONE are set to consolidate all MCI register verification in one place.
The verification process:
- MCU ROM configures
MBOX[0,1]_VALID_AXI_USERregisters with the expected AXI user values - MCU ROM sets
MBOX[0,1]_AXI_USER_LOCKto lock each configured slot (this makes the AXI user registers read-only) - MCU ROM sets
SS_CONFIG_DONE_STICKYandSS_CONFIG_DONE - MCU ROM verifies that both config done registers are actually set
- MCU ROM reads back the
MBOX[0,1]_VALID_AXI_USERregister values and compares them against the expected configuration - MCU ROM reads back the
MBOX[0,1]_AXI_USER_LOCKregister values and verifies they match the expected lock status - If any mismatch is detected in either the AXI user values or lock status, MCU ROM reports a fatal error (
ROM_SOC_MCU_MBOX_AXI_USER_VERIFY_FAILED)
Warm Reset Considerations
On warm reset, SS_CONFIG_DONE_STICKY remains set from the cold boot, so sticky registers are already locked and do not need to be reconfigured or verified. However, SS_CONFIG_DONE is cleared on warm reset, so:
- MCU ROM must set
SS_CONFIG_DONEafter configuring any non-sticky registers - MCU ROM must verify that
SS_CONFIG_DONEis actually set - Verification of sticky registers (PK hashes, MCU mailbox AXI users) is not required on warm reset since they are already locked
a6784b6
Device Ownership Transfer
This contains details about the Caliptra implementation of Device Ownership Transfer (DOT) with MCU to assist.
Device Ownership Transfer (DOT) is a security mechanism implemented in Caliptra that enables device owners to establish code signing capabilities rooted in the hardware root of trust without permanently burning the Code Authentication Key (CAK) into fuses. This provides flexibility in ownership management while maintaining strong security guarantees.
Reference: OCP Device Ownership Transfer specification.
Table of Contents
- Diagrams
- Glossary
- DOT Modes
- Cryptographic Binding Mechanism
- System Components
- State Machine
- Initialization Flow
- Runtime Commands
- Lifecycle Transitions
- Recovery Mechanisms
- Security Considerations
Diagrams
- ROM Startup and DOT State Initialization
- Recovery Mode and FMC Flow Management
- State Management
- Runtime Command: DOT_CAK_INSTALL
- Runtime Commands: DOT_LOCK / DOT_DISABLE
- Runtime Commands: DOT_UNLOCK_CHALLENGE / DOT_UNLOCK
- Lifecycle: Uninitialized β Volatile β Locked
- Unlock Flow: Locked β Volatile β Uninitialized
- Recovery: Handling Corrupted DOT_BLOB
Glossary
BMC (Baseboard Management Controller): System management controller that interfaces with Caliptra to issue DOT commands and manage recovery procedures.
CAK (Code Authentication Key): The public key used to authenticate firmware and code running on the device. This is the owner's code signing key rooted in Caliptra.
Caliptra: Hardware root of trust providing secure boot and cryptographic services.
Caliptra_Core: Component within Caliptra that performs cryptographic operations offload (key derivation, HMAC, signature verification), derives DOT_EFFECTIVE_KEY, authenticates DOT_BLOBs and commands, and manages owner public key hash.
Caliptra_MCU: Microcontroller component that manages DOT state machine, handles runtime commands, controls fuse burning operations, coordinates with Caliptra_Core, and manages Ownership_Storage.
DOT (Device Ownership Transfer): Security mechanism for flexible ownership management that enables device owners to establish code signing capabilities rooted in hardware without permanently burning keys into fuses.
DOT_BLOB: A cryptographically authenticated data structure containing the CAK and LAK, sealed with the DOT_EFFECTIVE_KEY via HMAC. Stored in external flash storage.
DOT_EFFECTIVE_KEY: A key derived from DOT_ROOT_KEY and the DOT_FUSE_ARRAY value, used to authenticate DOT_BLOBs via HMAC. The derivation varies based on EVEN/ODD state.
DOT_FUSE_ARRAY: A minimal fuse array using 1 bit per state change to track DOT state transitions. The fuse value acts as a counter that increments with each state change (one-time programmable).
DOT_ROOT_KEY: A hardware-derived secret key unique to the silicon, used as the basis for deriving DOT_EFFECTIVE_KEY. Provides silicon binding.
EVEN STATE: Uninitialized/Volatile state (fuse value % 2 == 0) where no persistent ownership is bound to the silicon.
FMC (First Mutable Code): First stage of mutable firmware. In Caliptra MCU architecture, FMC and RT are not differentiated as separate binaries (FMC is RT).
HMAC (Hash-based Message Authentication Code): Cryptographic authentication method used to seal and verify DOT_BLOBs.
LAK (Lock Authentication Key): The private key used to lock/unlock the DOT state and control the Disabled state. The entity possessing LAK.priv has the authority to:
- Lock ownership to the device (DOT_LOCK)
- Disable DOT while maintaining ownership (DOT_DISABLE)
- Unlock and release ownership (DOT_UNLOCK)
ODD STATE: Locked/Disabled state (fuse value % 2 == 1) where ownership is cryptographically bound to the silicon via DOT_BLOB.
Ownership_Storage: Volatile memory (e.g., FLOP-based register) that stores the current CAK and LAK during runtime. Must be retained across at least one MCU reset level and invalidated power cycle. Contents are not updatable once marked valid by a non-Caliptra entity. Also stores desired DOT_FUSE_ARRAY state for pending transitions.
ROM (Read-Only Memory): Immutable boot code that executes first on device startup.
RT (Runtime): Main operating firmware that executes after ROM and FMC initialization.
VendorKey: Vendor master key used for DOT_OVERRIDE operations in catastrophic recovery scenarios. Highest privilege key in the system.
Volatile DOT: Operating mode where CAK is installed per boot cycle and ownership is lost on power cycle. No fuse burning required.
Mutable Locking DOT: Operating mode where ownership is locked and bound to silicon via cryptographic binding using DOT_FUSE_ARRAY. Persists across power cycles.
Goals
- Enable owner-specific code signing rooted in Caliptra (the root of trust)
- Avoid permanent fuse programming for ownership keys
- Support both temporary (volatile) and persistent (mutable locking) ownership models
- Provide secure ownership transfer mechanisms
- Enable recovery from corrupted states
DOT Modes
1. Volatile DOT
Characteristics:
- CAK is installed per boot cycle
- Ownership information is stored only in Ownership_Storage
- Power cycle clears ownership
- No fuse burning required
- DOT_FUSE_ARRAY remains in EVEN state
Use Cases:
- Temporary ownership scenarios
Flow:
Uninitialized (EVEN) β [DOT_CAK_INSTALL] β Volatile (EVEN) β [Power Cycle] β Uninitialized (EVEN)
2. Mutable Locking DOT
Characteristics:
- CAK is locked and bound to silicon via cryptographic binding
- Uses DOT_FUSE_ARRAY to create cryptographic binding (1 bit per state change)
- Ownership persists across power cycles
- Requires LAK authentication for lock/unlock/disable operations
- DOT_BLOB stored in external storage (flash)
- State transitions require fuse burning
- Supports both Locked (with CAK) and Disabled (without CAK) states
Use Cases:
- Long-term ownership binding
Flow:
Uninitialized (EVEN) β Volatile (EVEN) β [DOT_LOCK] β Locked (ODD) β [DOT_UNLOCK] β Volatile (EVEN) β Uninitialized (EVEN)
β [DOT_DISABLE] β Disabled (ODD) β [DOT_UNLOCK] β Uninitialized (EVEN)
Cryptographic Binding Mechanism
The mutable locking DOT mechanism achieves secure binding without secure storage through a clever cryptographic construction:
Key Derivation
The DOT_EFFECTIVE_KEY is derived as follows:
DOT_EFFECTIVE_KEY = KDF(DOT_ROOT_KEY, DOT_FUSE_ARRAY_VALUE)
State-Dependent Derivation
In EVEN STATE (n):
- DOT_EFFECTIVE_KEY is derived using DOT_FUSE_ARRAY value (n+1)
- This represents the key that will seal the NEXT DOT_BLOB
- Allows pre-computation before fuse burning
In ODD STATE (n):
- DOT_EFFECTIVE_KEY is derived using DOT_FUSE_ARRAY value (n)
- This represents the key that authenticates the CURRENT DOT_BLOB
- Used to verify the stored DOT_BLOB on boot
DOT_BLOB Authentication
DOT_BLOB = {CAK, LAK, metadata}
HMAC_TAG = HMAC-SHA-512(DOT_EFFECTIVE_KEY, DOT_BLOB)
The DOT_BLOB is authenticated on every boot in ODD state to ensure the CAK and LAK is authentic.
Security Properties
- Binding to Silicon: DOT_ROOT_KEY is unique per device, preventing DOT_BLOB portability
- Binding to State: Fuse value is incorporated into key derivation, preventing rollback attacks
- Forward Security: Unlocking increments fuses, invalidating old DOT_BLOBs
- No Secure Storage Required: Cryptographic binding replaces need for secure non-volatile storage
System Components
Caliptra_MCU
- Manages DOT state machine
- Handles runtime commands
- Controls fuse burning operations
- Coordinates with Caliptra_Core for cryptographic operations
- Manages Ownership_Storage
Caliptra_Core
- Performs cryptographic operations offload(key derivation, HMAC, signature verification)
- Derives DOT_EFFECTIVE_KEY
- Authenticates DOT_BLOBs and commands
- Manages owner public key hash (SET_OWNER_PK_HASH)
DOT_FUSE_ARRAY
- Hardware fuse array
- Stores state counter (increments from 0 to maximum)
- Read during initialization
- Written during state transitions
- One-time programmable (OTP) per bit
Ownership_Storage
- Volatile storage for current CAK and LAK (ex, FLOP based register)
- Content must be retained across at least one MCU reset level; should be retained across as many MCU reset levels as possible
- Ownership data must be invalidated and/or scrubbed on power cycle
- Ownership data must not be updatable once marked as valid by a non-Caliptra entity
- Stores desired DOT_FUSE_ARRAY state for pending transitions
Storage (Flash)
- Non-volatile external storage
- Stores DOT_BLOB (with redundancy)
- Not assumed to be secure
BMC (Baseboard Management Controller)
- Issues DOT commands
- Manages recovery procedures
- Handles reset coordination
- May maintain backup DOT_BLOBs
State Machine
States
1. Uninitialized (EVEN State)
- DOT_FUSE_ARRAY is in EVEN state
- No CAK in Ownership_Storage
- Device boots without owner authentication
- No DOT_BLOB exists
2. Volatile (EVEN State)
- DOT_FUSE_ARRAY is in EVEN state
- CAK present in Ownership_Storage
- Device boots with owner authentication
- Ownership lost on power cycle
- No DOT_BLOB present
3. Locked (ODD State)
- DOT_FUSE_ARRAY is in ODD state
- CAK present in Ownership_Storage (retrieved from DOT_BLOB)
- DOT_BLOB authenticated
- Device boots with owner authentication
- Ownership persists across power cycles
- DOT_BLOB present in storage
4. Disabled (ODD State)
- DOT_FUSE_ARRAY is in ODD state
- LAK present in DOT_BLOB, but no CAK
- Device boots without code authentication enforcement
- Ownership is locked to silicon (via LAK) preventing unauthorized takeover
- Useful when owner doesn't want code signing but wants to prevent others from claiming ownership
5. Corrupted (ODD State)
- DOT_FUSE_ARRAY is in ODD state
- DOT_BLOB is corrupted or missing
- Device boots into recovery mode (ROM or FMC)
- No CAK available
- Special recovery or override commands accepted
State Transitions
βββββββββββββββββββ
β Uninitialized β (EVEN, n)
β No CAK β
ββββββββββ¬βββββββββ
β
βββββββββββββββββββββββββββββββββββββββ
β DOT_CAK_INSTALL + Reset β DOT_DISABLE + Reset + Fuse Burn
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ
β Volatile β (EVEN, n) β Disabled β (ODD, n+1)
β CAK in RAM β β LAK from BLOB β
ββββββββββ¬βββββββββ β No CAK β
β DOT_LOCK + Reset ββββββββββ¬βββββββββ
β + Fuse Burn β DOT_UNLOCK + Reset + Fuse Burn
βΌ β
βββββββββββββββββββ β
β Locked β (ODD, n+1) β
β CAK from BLOB β β
ββββββββββ¬βββββββββ β
β DOT_UNLOCK + Reset + Fuse Burn β
βΌ β
βββββββββββββββββββ β
β Volatile β (EVEN, n+2) β
β CAK in RAM β β
ββββββββββ¬βββββββββ β
β Power Cycle β
βββββββββββββββ¬βββββββββββββββββββββββ
βΌ
βββββββββββββββββββ
β Uninitialized β (EVEN, n+2)
β No CAK β
βββββββββββββββββββ
Initialization Flow
Boot Sequence (DOT ROM Startup)
-
MCU ROM Startup
- Caliptra_MCU boots and starts MCU ROM
- MCU ROM initializes Caliptra_Core
-
Read DOT_FUSE_ARRAY State
- MCU ROM reads current DOT_FUSE_ARRAY value (n)
- Determines if state is EVEN or ODD
-
Derive DOT_EFFECTIVE_KEY
- MCU ROM calls Caliptra_Core ROM to derive key
- If EVEN state: Derive with (n+1) for next DOT_BLOB sealing
- If ODD state: Derive with (n) for current DOT_BLOB authentication
-
ODD State Processing
- Read DOT_BLOB from storage
- Authenticate DOT_BLOB using HMAC with DOT_EFFECTIVE_KEY
- If authentic: Extract CAK/LAK and program into Ownership_Storage
- If corrupted: Boot into DOT recovery mode (recovery flow documented in later diagram)
-
Set Owner Public Key
- Read CAK from Ownership_Storage (if present)
- Call Caliptra_Core ROM SET_OWNER_PK_HASH with CAK
-
Boot Firmware
- Call Caliptra_Core ROM RI_DOWNLOAD_FIRMWARE
- MCU ROM resets and jumps to RT (Runtime)
State Determination Logic
if (DOT_FUSE_ARRAY % 2 == 0) {
// EVEN STATE - Unlocked/Disabled
DOT_EFFECTIVE_KEY = Derive(DOT_ROOT_KEY, n+1)
// Ready to seal new DOT_BLOB
} else {
// ODD STATE - Locked/Enabled
DOT_EFFECTIVE_KEY = Derive(DOT_ROOT_KEY, n)
DOT_BLOB = Read_Storage()
if (Authenticate(DOT_BLOB, DOT_EFFECTIVE_KEY)) {
CAK, LAK = Extract(DOT_BLOB)
Write_Ownership_Storage(CAK, LAK)
} else {
Boot_Recovery_Mode()
}
}
Runtime Commands
1. DOT_CAK_INSTALL
Purpose: Install CAK for volatile DOT ownership.
Preconditions:
- No existing ownership (Ownership_Storage empty)
- DOT_FUSE_ARRAY is in EVEN state
Flow:
- BMC issues DOT_CAK_INSTALL command with CAK (and optionally LAK)
- MCU RT checks Ownership_Storage for existing ownership
- If ownership exists: Return error
- MCU RT writes CAK/LAK to Ownership_Storage
- Request subsystem reset (lower level reset that should not clear out Ownership_Storage)
- On next boot, CAK will be active for firmware authentication
Result: Device enters Volatile state with CAK active until power cycle.
2. DOT_LOCK
Purpose: Lock current volatile ownership to silicon, creating mutable locking DOT.
Preconditions:
- DOT_FUSE_ARRAY in EVEN state
- CAK present in Ownership_Storage
Flow:
- BMC issues DOT_LOCK command (signed with LAK.priv)
- MCU RT checks DOT_FUSE_ARRAY state
- If ODD state: Return error (already locked)
- MCU RT authenticates command with LAK.pub
- If authentication fails: Return error
- MCU RT creates DOT_BLOB containing CAK and LAK
- MCU RT seals DOT_BLOB with HMAC-SHA-512(DOT_EFFECTIVE_KEY, DOT_BLOB)
- DOT_EFFECTIVE_KEY was derived with (n+1) during boot
- Write DOT_BLOB to storage (primary and redundant copies)
- Update Ownership_Storage with desired DOT_FUSE_ARRAY state = (n+1)
- Return success and request subsystem reset
- On next boot (see State Management, diagram reference:
dot-3-state):- ROM/FMC reads Ownership_Storage desired state
- Verifies DOT_BLOB is valid in storage
- Burns DOT_FUSE_ARRAY from (n) to (n+1)
- Requests another reset
- On subsequent boot:
- Device boots in ODD state (n+1)
- DOT_BLOB is authenticated and CAK/LAK retrieved
- Device is now in Locked state
Result: Device enters Locked state with ownership persisting across power cycles.
3. DOT_DISABLE
Diagram is above.
Purpose: Lock DOT mechanism in disabled state directly from uninitialized state, maintaining ownership control without code authentication requirements.
Use Case: When the owner does not want to sign code with CAK but also does not want to leave the system in an uninitialized state where others could take over ownership. This provides a secure "parked" state where:
- The device remains under the owner's control (via LAK)
- No code authentication is enforced (no CAK)
- Unauthorized parties cannot install their own CAK or disable the device
- The owner can later unlock to return to uninitialized state
Preconditions:
- DOT_FUSE_ARRAY in EVEN state
- Device in Uninitialized state (no ownership in Ownership_Storage)
- LAK provided in command
Flow:
- BMC issues DOT_DISABLE command with LAK (signed with LAK.priv)
- MCU RT checks DOT_FUSE_ARRAY state
- If ODD state: Return error (already locked or disabled)
- MCU RT checks Ownership_Storage is empty (Uninitialized state)
- If ownership exists: Return error
- MCU RT authenticates command with LAK.pub
- If authentication fails: Return error
- MCU RT creates DOT_BLOB containing LAK but no CAK
- MCU RT seals DOT_BLOB with HMAC-SHA512(DOT_EFFECTIVE_KEY, DOT_BLOB)
- Write DOT_BLOB to storage (primary and redundant copies)
- Update Ownership_Storage with desired DOT_FUSE_ARRAY state = (n+1)
- Return success and request subsystem reset
- On next boot (see State Management):
- ROM/FMC reads Ownership_Storage desired state
- Verifies DOT_BLOB is valid in storage
- Burns DOT_FUSE_ARRAY from (n) to (n+1)
- Requests another reset
- On subsequent boot:
- Device boots in ODD state (n+1)
- DOT_BLOB is authenticated and LAK recovered (but no CAK)
- Device is now in Disabled state
Result: Device enters Disabled state - ownership is locked to the silicon (preventing takeover) but no code authentication is active. The owner retains authority to unlock via LAK.priv, which will return the device to Uninitialized state.
4. DOT_UNLOCK_CHALLENGE / DOT_UNLOCK
Purpose: Unlock and unbind ownership from silicon, returning to volatile or uninitialized state.
Preconditions:
- DOT_FUSE_ARRAY in ODD state (Locked or Disabled)
- Valid DOT_BLOB in storage (for LAK.pub)
Flow:
- BMC issues DOT_UNLOCK_CHALLENGE
- MCU RT checks DOT_FUSE_ARRAY state
- If EVEN state: Return error (already unlocked)
- MCU RT generates and returns challenge
- Challenge based on Unlock_Method from previous LOCK/DISABLE
- BMC provides signed challenge with LAK.priv
- BMC issues DOT_UNLOCK command with signed challenge
- MCU RT verifies challenge matches
- MCU RT authenticates command using LAK.pub from DOT_BLOB
- If authentication fails: Return error
- MCU RT updates Ownership_Storage with desired DOT_FUSE_ARRAY state = (n+1)
- Return success and request subsystem reset
- On next boot (see State Management):
- ROM/FMC reads Ownership_Storage desired state
- Burns DOT_FUSE_ARRAY from (n) to (n+1)
- Erases DOT_BLOB from storage (no longer needed)
- Requests another reset
- On subsequent boot:
- Device boots in EVEN state (n+1)
- If unlocked from Locked state: CAK still in Ownership_Storage β enters Volatile state
- If unlocked from Disabled state: No CAK in Ownership_Storage β enters Uninitialized state
Result:
- From Locked: Device enters Volatile state. Power cycle will return to Uninitialized.
- From Disabled: Device enters Uninitialized state immediately (no CAK was present).
State Management
State transitions require fuse burning, which is performed by ROM/FMC (not RT) for security reasons. The Ownership_Storage is used to communicate the desired state change from RT to ROM/FMC.
State Transition Protocol
Executed by ROM/FMC on boot:
- Check Ownership_Storage for desired DOT_FUSE_ARRAY state
- Verify desired state is exactly (n+1) where n is current state
- If transition from EVEN to ODD (LOCK/DISABLE):
- Read DOT_BLOB from storage
- Authenticate DOT_BLOB with DOT_EFFECTIVE_KEY
- If invalid: Abort (should not happen if RT did its job)
- Increment DOT_FUSE_ARRAY from (n) to (n+1)
- If transition from ODD to EVEN (UNLOCK/ENABLE):
- Increment DOT_FUSE_ARRAY from (n) to (n+1)
- Erase DOT_BLOB from storage
- Request subsystem reset
ROM vs FMC Implementation Options
The DOT specification allows integrators of Caliptra MCU to choose which component handles fuse burning and state transitions. This flexibility accommodates different security models and implementation constraints.
Option 1: ROM-Based State Transitions
Characteristics:
- ROM performs all fuse burning and state transition operations
- Provides a known, immutable, and trusted implementation
- State transitions occur in the most trusted execution environment
Advantages:
- ROM provides a sane and known state
- Immutable code reduces attack surface
- Consistent behavior across all boots
Disadvantages:
- Requires complex and error-prone code (fuse burning) to be in ROM
- ROM must implement complicated protocols with BMC or other EC
- Any bugs or limitations in ROM cannot be fixed without hardware revision
- Less flexibility for integrators to customize behavior
Option 2: FMC-Based State Transitions
Characteristics:
- FMC/RT performs fuse burning and state transition operations
- Note: In Caliptra MCU architecture, FMC and RT are not differentiated as separate binaries (FMC is RT)
- ROM indicates to FMC that it is in a DOT-related recovery/transition boot
Advantages:
- Gives integrators more flexibility to implement fuse burning in mutable code
- Easier to update and fix issues in the field
- Simplifies ROM implementation
- Allows customization of protocols and recovery flows
Disadvantages:
- Requires ROM to signal FMC about DOT recovery/transition boot mode
- FMC must be trusted to handle state transitions correctly
- FMC must block normal system boot and only conduct DOT recovery/override operations when in recovery mode
Requirements for FMC Implementation:
- ROM must indicate to FMC that it is in DOT-related recovery/transition boot
- FMC must not allow full system boot during DOT operations
- FMC must only accept DOT recovery/override commands in recovery mode
- FMC must properly validate and execute state transitions
Implementation Guidelines
Regardless of which option is chosen:
- Fuse burning is a sensitive operation and must not be performed by RT during normal operation
- The component performing fuse burning must validate DOT_BLOB before committing to fuse changes
- State transitions must be atomic (fuse + storage operations together)
- The system must be able to recover from partial state transitions
Lifecycle Transitions
Full Lifecycle: Uninitialized β Volatile β Locked
Phase 1: Uninitialized β Volatile
- Device boots with DOT_FUSE_ARRAY in EVEN state
- Ownership_Storage is empty
- No owner authentication active
- BMC issues DOT_CAK_INSTALL with CAK and LAK
- CAK/LAK written to Ownership_Storage
- Reset device
- Device boots with CAK active for authentication
- Result: Volatile ownership (lost on power cycle)
Phase 2: Volatile β Locked
- Device in Volatile state (EVEN, CAK in RAM)
- BMC issues DOT_LOCK command (signed with LAK.priv)
- Command authenticated with LAK.pub
- MCU RT creates DOT_BLOB with CAK and LAK
- DOT_BLOB sealed with HMAC using DOT_EFFECTIVE_KEY
- DOT_BLOB written to flash storage
- Ownership_Storage updated with desired state (n+1)
- Reset device
- ROM/FMC reads desired state from Ownership_Storage
- ROM/FMC validates DOT_BLOB in storage
- ROM/FMC burns DOT_FUSE_ARRAY from EVEN(n) to ODD(n+1)
- Reset device again
- Device boots in ODD state
- DOT_BLOB authenticated and CAK/LAK recovered to Ownership_Storage
- Result: Locked ownership (persists across power cycles)
Full Lifecycle: Locked β Volatile β Uninitialized
Phase 1: Locked β Volatile
- Device in Locked state (ODD, CAK from DOT_BLOB)
- BMC issues DOT_UNLOCK_CHALLENGE
- MCU RT returns challenge
- BMC signs challenge with LAK.priv
- BMC issues DOT_UNLOCK command with signed challenge
- MCU RT verifies challenge and authenticates with LAK.pub
- Ownership_Storage updated with desired state (n+1)
- Reset device
- ROM/FMC reads desired state
- ROM/FMC burns DOT_FUSE_ARRAY from ODD(n) to EVEN(n+1)
- ROM/FMC erases DOT_BLOB from storage
- Reset device again
- Device boots in EVEN state with CAK still in Ownership_Storage
- Result: Volatile ownership (temporary)
Phase 2: Volatile β Uninitialized
- Device in Volatile state (EVEN, CAK in RAM)
- BMC power cycles device
- Ownership_Storage contents lost
- Device boots with no CAK
- Result: Uninitialized state (no ownership)
Recovery Mechanisms
Failure scenario in dot-1-init
Detection:
- Device in ODD state (should have valid DOT_BLOB)
- DOT_BLOB read from storage
- HMAC authentication fails
Recovery Flow:
- MCU ROM detects corrupted DOT_BLOB during initialization
- MCU ROM boots MCU RT without CAK/LAK
- MCU RT flag indicates DOT recovery mode
- RT only accepts recovery commands (no normal boot)
- BMC detects silicon in DOT recovery mode
- Via boot progress tracking
- Via error codes
- Via explicit notification
- MCU ROM/FMC conduct DOT recovery flow
- Option 1: Recovery (BMC or EC has backup DOT_BLOB)
- Option 2: Override (Vendor only command)
Option 1: DOT_RECOVERY Command
When: BMC has a backup copy of the DOT_BLOB
Flow:
- BMC issues DOT_RECOVERY command with backup DOT_BLOB
- MCU authenticates DOT_BLOB with DOT_EFFECTIVE_KEY
- If authentication fails: Return error and abort
- MCU writes authenticated DOT_BLOB to flash
- Request subsystem reset
- On next boot, DOT_BLOB will be valid
- Result: Device returns to Locked state with recovered ownership
Requirements:
- BMC must maintain backup DOT_BLOB
- DOT_BLOB must match current DOT_FUSE_ARRAY state
- Cannot recover if backup is also corrupted
Challenge/Response Recovery via MCI Mailbox
An example specific implementation of the DOT_RECOVERY method using the MCI mailbox is provided below.
When: The platform provides an external recovery agent (e.g., BMC or host) that controls (or can ask for signatures) using the vendor ECDSA P-384 and MLDSA-87 private keys.
The reference MCU ROM uses MCI mailbox 0 (mcu_mbox0) to perform a
two-transaction challenge/response protocol with the external agent.
The agent provides public keys and signs the challenge externally; the ROM
verifies the signatures using Caliptra's CM_ECDSA384_VERIFY and
CM_MLDSA87_VERIFY mailbox commands.
Protocol:
Transaction 1: DOT_RECOVERY_REQUEST
Command Code: 0x444F_5451 ("DOTQ")
Table: DOT_RECOVERY_REQUEST input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum (Caliptra standard formula) |
| ecc_pub_key_x | u8[48] | ECDSA P-384 public key X coordinate |
| ecc_pub_key_y | u8[48] | ECDSA P-384 public key Y coordinate |
| mldsa_pub_key | u8[2592] | MLDSA-87 public key |
| cak | u8[48] | New Code Authentication Key hash |
| lak | u8[48] | New Lock Authentication Key hash |
Table: DOT_RECOVERY_REQUEST output arguments
| Name | Type | Description |
|---|---|---|
| challenge | u8[48] | Random 48-byte challenge for signing |
The ROM computes SHA-384 of the concatenated public keys (ECC X β ECC Y β MLDSA)
and verifies the hash against the vendor recovery PK hash stored in OTP fuses.
If the hash matches, the ROM generates a random 48-byte challenge and returns it
via DataReady status.
Transaction 2: DOT_CHALLENGE_RESPONSE
Command Code: 0x444F_5452 ("DOTR")
Table: DOT_CHALLENGE_RESPONSE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum (Caliptra standard formula) |
| ecc_sig_r | u8[48] | ECDSA P-384 signature R component |
| ecc_sig_s | u8[48] | ECDSA P-384 signature S component |
| mldsa_pub_key | u8[2592] | MLDSA-87 public key |
| mldsa_signature | u8[4627] | MLDSA-87 signature |
| padding | u8 | Padding for MLDSA signature |
The ECDSA signature is over SHA-384(challenge). The MLDSA signature is
over the raw challenge bytes.
The ROM verifies both signatures over the challenge. The ROM
intentionally does not set CmdComplete after reading this transaction
so that the SRAM data remains valid during signature verification (the
MLDSA pub key and signature are read directly from SRAM in zero-copy mode).
Flow:
- ROM boots and detects DOT recovery mode (corrupted DOT_BLOB)
- ROM polls MCI mbox0 for
DOT_RECOVERY_REQUESTcommand - Agent writes pub keys + new DOT blob data β mbox0 SRAM
- ROM hashes pub keys and verifies against vendor recovery PK hash in OTP
- ROM generates random 48-byte challenge β responds via mbox0
DataReady - Agent signs challenge with ECC and MLDSA private keys
- Agent writes signatures + MLDSA pub key β mbox0 as
DOT_CHALLENGE_RESPONSE - ROM verifies ECC signature via
CM_ECDSA384_VERIFY - ROM verifies MLDSA signature via
CM_MLDSA87_VERIFY - If both pass: ROM writes new DOT_BLOB and requests subsystem reset
- Result: Device returns to Locked state with new ownership keys
Requirements:
- External agent must hold vendor ECDSA P-384 and MLDSA-87 private keys
- Vendor recovery PK hash must be burned in OTP fuses
- MCI mbox0 must be accessible to the agent
- Public key hash must match the OTP vendor recovery PK hash
Option 2: DOT_OVERRIDE Command
When: BMC does not have backup DOT_BLOB (catastrophic recovery, RMA to vendor)
Flow:
- BMC issues DOT_UNLOCK_CHALLENGE
- MCU returns challenge
- BMC signs challenge with Vendor private key (VendorKey.priv)
- BMC issues DOT_OVERRIDE command with signed challenge
- MCU verifies challenge matches
- MCU authenticates command with Vendor public key (VendorKey.pub)
- If authentication fails: Return error and abort
- MCU burns DOT_FUSE_ARRAY from ODD(n) to EVEN(n+1)
- This is an ODD to EVEN transition only
- Request subsystem reset
- On next boot, device is in EVEN state (Uninitialized)
- Result: Device unlocked, but ownership lost (factory reset equivalent)
Recovery State Machine
Locked (ODD, n) + Corrupted BLOB
β
Recovery Mode
βββ [DOT_RECOVERY with valid backup] β Locked (ODD, n) [restored]
βββ [Challenge/Response via MCI mbox0] β Locked (ODD, n) [new ownership]
Security Considerations
Security Properties
-
Silicon Binding
- DOT_ROOT_KEY is unique per device, for Caliptra 2.0/2.1, it is up to the integrator to decide how to form this per-device unique key.
- DOT_BLOBs cannot be authenticated / used between devices of the same model
- Each device requires its own unique DOT_BLOB
-
State Binding
- DOT_EFFECTIVE_KEY incorporates fuse state
- Old DOT_BLOBs become invalid after unlock
- Prevents rollback to previous ownership states
-
Forward Security
- Unlocking increments fuses
- Previous DOT_BLOBs cannot be replayed
- Each lock operation requires new DOT_BLOB
-
Authenticity
- DOT_LOCK requires LAK.priv signature
- DOT_DISABLE requires LAK.priv signature
- DOT_UNLOCK requires LAK.priv signature
- DOT_OVERRIDE requires VendorKey.priv signature
- All commands that touches fuse state are cryptographically authenticated
-
Ownership Protection
- Disabled state prevents unauthorized ownership takeover
- Owner maintains control via LAK even without CAK enforcement
- Uninitialized devices can be claimed by any party, Disabled devices cannot
-
Minimal Fuse Usage
- Only 1 bit per state change
- Efficient use of limited fuse resources
- Supports many lock/unlock cycles
Threat Model
Protected Against:
- Unauthorized ownership changes (requires LAK.priv)
- DOT_BLOB tampering (HMAC-authenticated)
- DOT_BLOB replay from different device (silicon binding)
- DOT_BLOB replay from previous state (state binding)
- Rollback attacks (fuse increment prevents rollback)
- Storage attacks (cryptographic binding, no trust in storage)
Not Protected Against:
- Physical attacks on fuses (assumed secure)
- Compromise of DOT_ROOT_KEY (silicon security boundary)
- Compromise of LAK.priv (ownership credential)
- Compromise of VendorKey.priv (vendor master key)
- Side-channel attacks on cryptographic operations
Best Practices
-
LAK Management
- Store LAK.priv securely (HSM, secure enclave)
- Control LAK.priv access carefully
- Consider key escrow for recovery scenarios
- Implement proper key rotation when re-locking
-
DOT_BLOB Backup
- Maintain redundant backups of DOT_BLOB
- Store in multiple locations
- Verify backups periodically
- Secure backup storage (integrity and availability)
-
State Transition Management
- Verify DOT_BLOB integrity before DOT_LOCK fuse burn
- Ensure storage is reliable
- Handle reset failures gracefully
- Monitor fuse budget (total available transitions)
-
Recovery Planning
- Define recovery procedures between device and BMC or EC
- Test recovery scenarios
- Maintain secure access to VendorKey.priv
- Document override procedures
-
Operational Security
- Audit DOT command invocations
- Monitor DOT state changes
- Alert on recovery mode entries
- Track fuse usage over time
Fuse Budget Planning
Each state transition consumes one fuse bit:
Uninitialized(0) β Volatile(0) β Locked(1) β Volatile(2) β Locked(3) β ...
With N fuse bits:
- Maximum N state transitions
- Approximately N/2 lock/unlock cycles
- Plan for expected device lifetime
- Reserve bits for recovery scenarios
Example with 128-bit fuse array:
- 256 total state transitions
- ~128 lock/unlock cycles
- More than sufficient for most use cases
Appendix: Command Reference
Command Summary Table
| Command | State Requirement | Authentication | Fuse Impact | Purpose |
|---|---|---|---|---|
| DOT_CAK_INSTALL | EVEN | None | No | Install volatile ownership |
| DOT_LOCK | EVEN | LAK.priv | Yes (nβn+1) | Lock ownership to silicon |
| DOT_DISABLE | EVEN | LAK.priv | Yes (nβn+1) | Disable DOT in locked state |
| DOT_UNLOCK_CHALLENGE | ODD | None | No | Request unlock challenge |
| DOT_UNLOCK | ODD | LAK.priv | Yes (nβn+1) | Unlock ownership from silicon |
| DOT_RECOVERY | ODD (Recovery) | DOT_BLOB HMAC | No | Restore corrupted DOT_BLOB |
| DOT_OVERRIDE | ODD (Recovery) | VendorKey.priv | Yes (nβn+1) | Force unlock (destructive) |
| DOT_RECOVERY_REQUEST | ODD (Recovery) | VendorKey (ECC+MLDSA) | No | Challenge/response recovery |
State Transition Table
| From State | Command | To State | Fuse Change | Storage Change |
|---|---|---|---|---|
| Uninitialized (EVEN) | DOT_CAK_INSTALL | Volatile (EVEN) | No | None |
| Volatile (EVEN) | Power Cycle | Uninitialized (EVEN) | No | None |
| Volatile (EVEN) | DOT_LOCK | Locked (ODD) | EVENβODD | Create DOT_BLOB |
| Volatile (EVEN) | DOT_DISABLE | Disabled (ODD) | EVENβODD | Create DOT_BLOB (no CAK) |
| Locked (ODD) | DOT_UNLOCK | Volatile (EVEN) | ODDβEVEN | Delete DOT_BLOB |
| Locked (ODD) | Corrupted BLOB | Recovery (ODD) | No | None |
| Recovery (ODD) | DOT_RECOVERY | Locked (ODD) | No | Restore DOT_BLOB |
| Recovery (ODD) | DOT_RECOVERY_REQUEST | Locked (ODD) | No | New DOT_BLOB via challenge/response |
| Recovery (ODD) | DOT_OVERRIDE | Uninitialized (EVEN) | ODDβEVEN | Delete DOT_BLOB |
a6784b6
Runtime Specification
The MCU runtime firmware is based on the Tock real-time, embedded operation system. Tock provides the ability for us to run multiple high-level applications concurrently and securely. For more specific information on how Tock internals work, please see the Tock kernel documentation.
Any RISC-V code that needs to run in M-mode, e.g., low-level drivers, should run in the Tock board or a capsule loaded by the board.
In Tock, the "board" is the code that runs that does all of the hardware initialization and starts the Tock kernel. The Tock board is essentially a custom a kernel for each SoC.
The applications are higher-level RISC-V code that only interact with the rest of the world through Tock system calls. For instance, an app might be responsible for running a PLDM flow and uses a Tock capsule to interact with the MCTP stack to communicate with the rest of the SoC.
Architecture
The overall architecture for the MCU firmware stack is thought of in layers:
- At the highest layer are the user-mode applications that run specific flows that are relevant for vendors. These run more complex, dynamic protocols, like PLDM and SPDM.
- These interact with common user-mode APIs for protocols like MCTP. These handle the details of converting low-level system calls to the Tock kernel and capsules into synchronous and asynchronous APIs.
- Neither the applications nor the user-mode APIs have the ability to access hardware directly.
- The Tock kernel is responsible for scheduling the applications and routing system calls to the appropriate capsules. Everything at the Tock kernel level and below execute in machine mode with full privileges.
- The capsules are Tock kernel modules that implement specific specific workflows and capabilities, like using the MCTP stack or accessing the Caliptra mailbox.
- Everything at the capsule layer and above should be independent of the hardware specifics. Everything below the capsule layer is specific to the hardware implementations.
- The capsules in turn talk to specific drivers. These are generally implementations of specific Rust traits, like Tock HILs, that provide access to hardware.
- Two of the most fundamental pieces of Rust code sit at the bottom: the chip and board files. The chip file contains microcontroller-specific code, such as dealing with interrupts, can should be able to be reused on different boards that use the same microcontroller.
- The board file is the heart of Tock: it is the
main()function, and is responsible for creating and initializing all of the hardware drivers, the chip file, creating the Tock kernel, loading and configuring the capsules, and starting the main execution loop.
a6784b6
PLDM Update Package
This section describes the PLDM Update Package used for Caliptra streaming boot and firmware update.
The update package follows the DMTF Firmware Update Specification v.1.3.0.
| PLDM FW Update Package |
|---|
| Package Header Information |
| Firmware Dev ID Descriptors |
| Downstream Dev ID Descriptors |
| Component Image Information |
| Package Header Checksum |
| Package Payload Checksum |
| Component 1 (Caliptra FMC + RT) |
| Component 2 (SoC Manifest) |
| Component 3 (MCU RT) |
| Component 4 (SoC Image 1) |
| ... |
| Component N (SoC Image N-3) |
| Component N + 1 (Streaming Boot Image for full flash) |
The PLDM FW Update Package contains:
- Caliptra FMC (First Mutable Code) and RT (Runtime) Image Bundle
- The SOC Manifest for the MCU RT image and other SOC Images
- The MCU RT image
- Other SOC Images, if any
- A full image of the flash containing all images (see SPI Flash Layout Documentation)
Note: All fields are little-endian byte-ordered unless specified otherwise.
Package Header Information
| Field | Size | Definition |
|---|---|---|
| PackageHeaderIdentifier | 16 | Set to 0x7B291C996DB64208801B0202E6463C78 (v1.3.0 UUID) (big endian) |
| PackageHeaderFormatRevision | 1 | Set to 0x04 (v1.3.0 header format revision) |
| PackageHeaderSize | 2 | The total byte count of this header structure, including fields within the Package Header Information, |
| Firmware Device Identification Area, Downstream Device Identification Area, | ||
| Component Image Information Area, and Checksum sections. | ||
| PackageReleaseDateTime | 13 | The date and time when this package was released. |
| Refer to the PLDM Base Specification for field format definition. | ||
| ComponentBitmapBitLength | 2 | Number of bits used to represent the bitmap in the ApplicableComponents field for a matching device. |
| This value is a multiple of 8 and is large enough to contain a bit for each component in the package. | ||
| PackageVersionStringType | 1 | The type of string used in the PackageVersionString field. |
| Refer to DMTF Firmware Update Specification v.1.3.0 Table 33 for values. | ||
| PackageVersionStringLength | 1 | Length, in bytes, of the PackageVersionString field. |
| PackageVersionString | Variable | Package version information, up to 255 bytes. |
| Contains a variable type string describing the version of this firmware update package. |
Firmware Device ID Descriptor
| Field | Size | Definition |
|---|---|---|
| RecordLength | 2 | The total length in bytes for this record. The length includes the RecordLength, DescriptorCount, DeviceUpdateOptionFlags, ComponentImageSetVersionStringType, ComponentSetVersionStringLength, FirmwareDevicePackageDataLength, ApplicableComponents, ComponentImageSetVersionString, and RecordDescriptors, and FirmwareDevicePackageData fields. |
| DescriptorCount | 1 | The number of descriptors included within the RecordDescriptors field for this record. |
| DeviceUpdateOptionFlags | 4 | 32-bit field where each bit represents an update option. |
| bit 1 is set to 1 (Support Flashless/Streaming Boot) | ||
| For other options, refer to DeviceUpdateOptionFlags description in DMTF Firmware Update Specification v.1.3.0. | ||
| ComponentImageSetVersionStringType | 1 | The type of string used in the ComponentImageSetVersionString field. Refer to DMTF Firmware Update Specification v.1.3.0 Table 33 for values. |
| ComponentImageSetVersionStringLength | 1 | Length, in bytes, of the ComponentImageSetVersionString. |
| FirmwareDevicePackageDataLength | 2 | Length in bytes of the FirmwareDevicePackageData field. |
If no data is provided, set to 0x0000. | ||
| ReferenceManifestLength | 4 | Length in bytes of the ReferenceManifestData field. If no data is provided, set to 0x00000000. |
| ApplicableComponents | Variable | Bitmap indicating which firmware components apply to devices matching this Device Identifier record. A set bit indicates the Nth component in the payload is applicable to this device. |
| bit 0: Caliptra FMC + RT | ||
| bit 1: SOC Manifest | ||
| bit 2 : MCU RT | ||
| bit 3 to N: Reserved for other SoC images | ||
| bit N+1: Full SPI Flash image used in streaming boot | ||
| ComponentImageSetVersionString | Variable | Component Image Set version information, up to 255 bytes. |
| Describes the version of component images applicable to the firmware device indicated in this record. | ||
| RecordDescriptors | Variable | These descriptors are defined by the vendor (i.e. integrators of Caliptra ROT) |
| Refer to DMTF Firmware Update Specification v.1.3.0 Table 7 for details of these fields and the values that can be selected. | ||
| FirmwareDevicePackageData | Variable | Optional data provided within the firmware update package for the FD during the update process. |
If FirmwareDevicePackageDataLength is 0x0000, this field contains no data. | ||
| ReferenceManifestData | Variable | Optional data field containing a Reference Manifest for the firmware update. |
| If present, it describes the firmware update provided by this package. | ||
If ReferenceManifestLength is 0x00000000, this field contains no data. |
Downstream Device ID Descriptor
There are no Downstream Device ID records for this package
| Field | Size | Definition |
|---|---|---|
| DownstreamDeviceIDRecordCount | 1 | 0 |
Component Image Information
| Field | Size | Definition |
|---|---|---|
| ComponentImageCount | 2 | Count of individually defined component images contained within this firmware update package. |
| ComponentImageInformation | Variable | Contains details for each component image within this firmware update package. |
Component Image Information Record
| Field | Size | Definition |
|---|---|---|
| ComponentClassification | 2 | 0x000A: For Caliptra FMC+RT (Firmware) |
0x0001: For SoC Manifest (Other) | ||
0x000A: For MCU RT: (Firmware) | ||
| For other SoC images, Refer to DMTF Firmware Update Specification v.1.3.0 Table 32 for values. | ||
| ComponentIdentifier | 2 | Unique value selected by the FD vendor to distinguish between component images. |
0x0001: Caliptra FMC+RT | ||
0x0002: SoC Manifest: | ||
0x0003: MCU RT | ||
0x1000-0xFFFF - Reserved for other vendor-defined SoC images | ||
| ComponentComparisonStamp | 4 | Value used as a comparison in determining if a firmware component is down-level or up-level. When ComponentOptions bit 1 is set, this field should use a comparison stamp format (e.g., MajorMinorRevisionPatch). If not set, use 0xFFFFFFFF. |
| ComponentOptions | 2 | Refer to ComponentOptions definition in DMTF Firmware Update Specification v.1.3.0 |
| RequestedComponentActivationMethod | 2 | Refer to RequestedComponentActivationMethoddefinition inDMTF Firmware Update Specification v.1.3.0 |
| ComponentLocationOffset | 4 | Offset in bytes from byte 0 of the package header to where the component image begins. |
| ComponentSize | 4 | Size in bytes of the Component image. |
| ComponentVersionStringType | 1 | Type of string used in the ComponentVersionString field. Refer toDMTF Firmware Update Specification v.1.3.0 Table 33 for values. |
| ComponentVersionStringLength | 1 | Length, in bytes, of the ComponentVersionString. |
| ComponentVersionString | Variable | Component version information, up to 255 bytes. Contains a variable type string describing the component version. |
| ComponentOpaqueDataLength | 4 | Length in bytes of the ComponentOpaqueData field. If no data is provided, set to 0x00000000. |
| ComponentOpaqueData | Variable | Optional data transferred to the FD/FDP during the firmware update |
Checksum
| Field | Size | Definition |
|---|---|---|
| PackageHeaderChecksum | 4 | The integrity checksum of the PLDM Package Header. It is calculated starting at the first byte of the PLDM Firmware Update Header and includes all bytes of the package header structure except for the bytes in this field and the PackagePayloadChecksum field. The CRC-32 algorithm with polynomial 0x04C11DB7 (as used by IEEE 802.3) is used for checksum computation, processing one byte at a time with the least significant bit first. |
| PackagePayloadChecksum | 4 | The integrity checksum of the PLDM Package Payload. It is calculated starting at the first byte immediately following this field and includes all bytes of the firmware package payload structure, including any padding between component images. The CRC-32 algorithm with polynomial 0x04C11DB7 (as used by IEEE 802.3) is used for checksum computation, processing one byte at a time with the least significant bit first. |
Component 1 - Caliptra FMC + RT
This is the package bundle for the Caliptra FMC + RT defined in Caliptra Firmware Image Bundle Format.
Component 2 - SOC Manifest
This is the SoC Manifest defined in SoC Manifest.
It provides the signature verification and specific data needed to decode the image.
Component 3 - MCU RT
This is the Image binary of the MCU Realtime firmware.
Component 4 to N - SoC Images
These are reserved for vendor SoC images, if any.
Component N + 1 - Full SPI Flash image
This component contains the full flash image as defined in the SPI Flash Layout Documentation from the Header section until the end of the images section. This is can be used for loading all the images at once for streaming boot.
a6784b6
SPI Flash Layout
Overall, the SPI Flash consists of a Header, Checksum and an Image Payload (which includes the image information and the image binary).
The specific images of the flash consists of the Caliptra FW, MCU RT, SoC Manifest, and other SoC images, if any.
Layout
Note: All fields are little-endian byte-ordered unless specified otherwise.
A typical overall flash layout is:
| Flash Layout |
|---|
| Header |
| Payload |
The Payload contains the following fields:
| Payload |
|---|
| Image Info (Caliptra FMC + RT) |
| Image Info (SoC Manifest) |
| Image Info (MCU RT) |
| Image Info (SoC Image 1) |
| ... |
| Image Info (SoC Image N) |
| Caliptra FMC + RT Package |
| SoC Manifest |
| MCU RT |
| SoC Image 1 |
| ... |
| SoC Image N |
- Caliptra FMC and RT (refer to the Caliptra Firmware Image Bundle Format)
- SoC Manifest (refer to the description of the SoC Manifest)
- MCU RT: This is the image binary of the MCU Runtime firmware
- Other SoC images (if any)
Header
The Header section contains the metadata for the images.
| Field | Size (bytes) | Description |
|---|---|---|
| Magic Number | 4 | A unique identifier to mark the start of the header. The value must be 0x464C5348 ("FLSH" in ASCII) for flash or 0x54465450 ("TFTP" in ASCII) for Network Boot |
| Header Version | 2 | The header version format, allowing for backward compatibility if the package format changes over time. (Current version is 0x0002) |
| Image Count | 2 | The number of images contained in the Payload.Each image will have its own image information section. |
| Payload Offset | 4 | Offset in bytes of the header to where the first byte of the Payload is located. |
| Header Checksum | 4 | Checksum calculated for the header excluding this field |
Image Information
The Image Information section is repeated for each image and provides detailed manifest data specific to that image.
| Field | Size (bytes) | Descr |
|---|---|---|
| Identifier | 4 | Vendor selected unique value to distinguish between images. |
0x00000000: Caliptra FMC+RT | ||
0x00000001: SoC Manifest | ||
0x00000002: MCU RT0x00001000-0xFFFFFFFF - Reserved for other Vendor-defined SoC images |
| ImageLocationOffset | 4 | Offset in bytes from byte 0 of the header to where the image content begins. Used in flash-based boot. | Size | 4 | Size in bytes of the image. This is the actual size of the image without padding. | ||| The image itself as written to the flash should be 4-byte aligned and additional | ||| padding will be required to guarantee alignment. | | Filename | 64 | Used in network boot to specify the TFTP path relative to the TFTP server root. Field is not used in flash-based boot | | Image Checksum | 4 | Checksum calculated for the binary image. | | Image Info Checksum | 4 | Checksum calculated for the header excluding this field |
Image
The images (raw binary data) are appended after the Image Information section, and should be in the same order as their corresponding Image Information.
| Field | Size (bytes) | Description |
|---|---|---|
| Data | N | Image content. |
| Note: The image should be 4-byte aligned. | ||
| If the size of a firmware image is not a multiple of 4 bytes, | ||
0x00 padding bytes will be added to meet the alignment requirement. | ||
| Only used in flash-based boot |
a6784b6
Flash Controller
Overview
The Flash Controller stack in the Caliptra MCU firmware is designed to provide efficient and reliable communication with flash devices, which is the foundation to enable flash-based boot flow.
We will primarily be targetting SPI flash controllers, so we will use that as our primary example throughout.
This document outlines the different SPI flash configurations being supported, the stack architecture, component interface and userspace API to interact with the SPI flash stack.
SPI Flash Configurations
The SPI flash stack supports various configurations to cater to different use cases and hardware setups. The diagram below shows the flash configurations supported.
1. Single-Flash Configuration In this setup, a single SPI flash device is connected to the flash controller. Typically, the flash device is divided into two halves: the first half serves as the primary flash, storing the active running firmware image, while the second half is designated as the recovery flash, containing the recovery image. Additional partitions, such as a staging area for firmware updates, flash store for certificates and debug logging can also be incorporated into the primary flash.
2. Dual-Flash Configuration In this setup, two SPI flash devices are connected to the same flash controller using different chip selects. This configuration provides increased storage capacity and redundancy. Typically, flash device 0 serves as the primary flash, storing the active running firmware image and additional partitions such as a staging area for firmware updates, flash store for certificates and debug logging. Flash device 1 is designated as the recovery flash, containing the recovery image.
3. Multi-Flash Configuration In more complex systems, multiple flash controllers may be used, each with one or more SPI flash devices. This configuration provides flexibility and scalability. For example, a backup flash can be added to recover the SoC and provide more resiliency for the system.
Architecture
The SPI flash stack design leverages TockOS's kernel space support for flash and associated virtualizer layers. The stack, from top to bottom, comprises the flash userland API, flash partition capsule, flash virtualizer and vendor-specific flash controller layer. SPI flash stack architecture with simplified flash controller layer is shown in the diagram below.
-
Flash Userland API
- Provides syscall library for userspace applications to issue IO requests (read, write, erase) to flash devices. Userspace application will instantiate the syscall library with unique driver number for individual flash partition.
-
Flash Partition Capsule
- Defines the flash partition structure with offset and size, providing methods for reading, writing, and erasing arbitrary lengths of data within the partitions. Each partition is logically represented by a
FlashUser, which leverages the existing flash virtualizer layer to ensure flash operations are serialized and managed correctly. It also implementsSyscallDrivertrait to interact with the userland API.
- Defines the flash partition structure with offset and size, providing methods for reading, writing, and erasing arbitrary lengths of data within the partitions. Each partition is logically represented by a
-
Vendor-specific Flash controller Layer
- Flash controller driver implements the
kernel::hil::flash::Flashtrait, which defines the standard interface (read, write, erase) for flash page-based operations.- Additional methods provided in the flash controller driver include:
- Initializing the SPI flash device and configuring settings such as clock speed, address mode, and other parameters.
- Checking the status of the flash device, such as whether it is busy or ready for a new operation.
- Erasing larger regions of flash memory, such as sectors or blocks, in addition to individual pages.
- Reading the device ID, manufacturer ID, or other identifying information from the flash device.
- Retrieving information about the flash memory layout, such as the size of pages, sectors, and blocks from SFDP.
- Performing advanced read/write operations using specific commands supported by the flash device.
- Additional methods provided in the flash controller driver include:
- A flash controller virtualizer
VirtualFlashCtrlshould be implemented to support the configuration of multiple flash devices connected to the same flash controller via different chip selects. The diagram below shows the stack to enable this scenario.
- Flash controller driver implements the
Common Interfaces
Flash Userland API
It is defined in SPI flash syscall library to provide async interface (read, write, erase) to underlying flash devices.
/// spi_flash/src/lib.rs
///
/// A structure representing an asynchronous SPI flash memory interface.
struct AsyncSpiFlash {
// The driver number associated with this SPI flash interface.
driver_num: u32,
}
/// Represents an asynchronous SPI flash memory interface.
///
/// This struct provides methods to interact with SPI flash memory in an asynchronous manner,
/// allowing for non-blocking read, write, and erase operations.
impl AsyncSpiFlash {
/// Creates a new instance of `AsyncSpiFlash`.
///
/// # Arguments
///
/// * `driver_num` - The driver number associated with the SPI flash.
fn new(driver_num: u32) -> Self {};
/// Checks if the SPI flash exists.
///
/// # Returns
///
/// * `Ok(())` if the SPI flash exists.
/// * `Err(ErrorCode)` if there is an error.
fn exists() -> Result<(), ErrorCode> {};
/// Reads an arbitrary number of bytes from the flash memory.
///
/// This method reads `len` bytes from the flash memory starting at the specified `address`
/// and stores them in the provided `buf`.
///
/// # Arguments
///
/// * `address` - The starting address to read from.
/// * `len` - The number of bytes to read.
/// * `buf` - The buffer to store the read bytes.
///
/// # Returns
///
/// * `Ok(())` if the read operation is successful.
/// * `Err(ErrorCode)` if there is an error.
async fn read(&self, address: usize, len: usize, buf: &mut [u8]) -> Result<(), ErrorCode>;
/// Writes an arbitrary number of bytes to the flash memory.
///
/// This method writes the bytes from the provided `buf` to the flash memory starting at the
/// specified `address`.
///
/// # Arguments
///
/// * `address` - The starting address to write to.
/// * `buf` - The buffer containing the bytes to write.
///
/// # Returns
///
/// * `Ok(())` if the write operation is successful.
/// * `Err(ErrorCode)` if there is an error.
async fn write(&self, address: usize, buf: &[u8]) -> Result<(), ErrorCode>;
/// Erases an arbitrary number of bytes from the flash memory.
///
/// This method erases `len` bytes from the flash memory starting at the specified `address`.
///
/// # Arguments
///
/// * `address` - The starting address to erase from.
/// * `len` - The number of bytes to erase.
///
/// # Returns
///
/// * `Ok(())` if the erase operation is successful.
/// * `Err(ErrorCode)` if there is an error.
async fn erase(&self, address: usize, len: usize) -> Result<(), ErrorCode>;
}
Flash partition capsule
/// A structure representing a partition of a flash memory.
///
/// This structure allows for operations on a specific partition of the flash memory,
/// defined by a start address and a size.
///
/// # Type Parameters
/// - `F`: The type of the flash memory, which must implement the `Flash` trait.
///
/// # Fields
/// - `flash_user`: A reference to the `FlashUser` that provides access to the flash memory.
/// - `start_address`: The starting address of the flash partition.
/// - `size`: The size of the flash partition.
/// - `client`: An optional reference to a client that implements the `FlashPartitionClient` trait.
struct FlashPartition<F: Flash> {
flash_user: &FlashUser<F>,
start_address: usize,
size: usize,
client: OptionalCell<&dyn FlashPartitionClient>,
}
/// A partition of a flash memory device.
///
/// This struct represents a partition of a flash memory device, allowing
/// operations such as reading, writing, and erasing within the partition.
///
/// # Type Parameters
///
/// - `F`: A type that implements the `Flash` trait.
impl<F: Flash> FlashPartition<F> {
/// Creates a new `FlashPartition`.
///
/// # Arguments
///
/// - `flash_user`: A reference to the `FlashUser` that owns the flash memory device.
/// - `start_address`: The starting address of the partition within the flash memory device.
/// - `size`: The size of the partition in bytes.
///
/// # Returns
///
/// A new `FlashPartition` instance.
fn new(
flash_user: &FlashUser<, F>,
start_address: usize,
size: usize,
) -> FlashPartition<F> {}
/// Sets the client for the flash partition.
///
/// # Arguments
///
/// - `client`: A reference to an object that implements the `FlashPartitionClient` trait.
fn set_client(&self, client: &dyn FlashPartitionClient) {}
/// Reads data from the flash partition.
///
/// # Arguments
///
/// - `buffer`: A mutable reference to a buffer where the read data will be stored.
/// - `offset`: The offset within the partition from which to start reading.
/// - `length`: The number of bytes to read.
///
/// # Returns
///
/// A `Result` indicating success or an error code.
fn read(
&self,
buffer: &'static mut [u8],
offset: usize,
length: usize,
) -> Result<(), ErrorCode> {}
/// Writes data to the flash partition.
///
/// # Arguments
///
/// - `buffer`: A mutable reference to a buffer containing the data to be written.
/// - `offset`: The offset within the partition at which to start writing.
/// - `length`: The number of bytes to write.
///
/// # Returns
///
/// A `Result` indicating success or an error code.
fn write(
&self,
buffer: &'static mut [u8],
offset: usize,
length: usize,
) -> Result<(), ErrorCode> {}
/// Erases data from the flash partition.
///
/// # Arguments
///
/// - `offset`: The offset within the partition at which to start erasing.
/// - `length`: The number of bytes to erase.
///
/// # Returns
///
/// A `Result` indicating success or an error code.
fn erase(&self, offset: usize, length: usize) -> Result<(), ErrorCode> {}
}
/// Implementation of the `SyscallDriver` trait for the `FlashPartition` struct.
/// This implementation provides support for reading, writing, and erasing flash memory,
/// as well as allowing read/write and read-only buffers, and subscribing to callbacks.
impl<F: Flash> SyscallDriver for FlashPartition< F> {
///
/// Handles commands from userspace.
///
/// # Arguments
///
/// * `command_number` - The command number to execute.
/// * `arg1` - The first argument for the command.
/// * `arg2` - The second argument for the command.
/// * `process_id` - The ID of the process making the command.
///
/// # Returns
///
/// A `CommandReturn` indicating the result of the command.
///
/// Commands:
/// - `0`: Success (no operation).
/// - `1`: Read operation. Reads `arg2` bytes from offset `arg1`.
/// - `2`: Write operation. Writes `arg2` bytes to offset `arg1`.
/// - `3`: Erase operation. Erases `arg2` bytes from offset `arg1`.
/// - Any other command: Not supported.
fn command(
&self,
command_number: usize,
arg1: usize,
arg2: usize,
process_id: ProcessId,
) -> CommandReturn {};
///
/// Allows a process to provide a read/write buffer.
///
/// # Arguments
///
/// * `process_id` - The ID of the process providing the buffer.
/// * `readwrite_number` - The identifier for the buffer.
/// * `buffer` - The buffer to be used for read/write operations.
///
/// # Returns
///
/// A `Result` indicating success or failure.
///
/// Buffers:
/// - `0`: Write buffer.
/// - Any other buffer: Not supported.
fn allow_readwrite(
&self,
process_id: ProcessId,
readwrite_number: usize,
buffer: Option<WriteableProcessBuffer>,
) -> Result<(), ErrorCode>;
///
/// Allows a process to provide a read-only buffer.
///
/// # Arguments
///
/// * `process_id` - The ID of the process providing the buffer.
/// * `readonly_number` - The identifier for the buffer.
/// * `buffer` - The buffer to be used for read-only operations.
///
/// # Returns
///
/// A `Result` indicating success or failure.
///
/// Buffers:
/// - `0`: Read buffer.
/// - Any other buffer: Not supported.
fn allow_readonly(
&self,
process_id: ProcessId,
readonly_number: usize,
buffer: Option<ReadableProcessBuffer>,
) -> Result<(), ErrorCode>{}
///
/// Subscribes a process to a callback.
///
/// # Arguments
///
/// * `subscribe_number` - The identifier for the callback.
/// * `callback` - The callback to be subscribed.
/// * `process_id` - The ID of the process subscribing to the callback.
///
/// # Returns
///
/// A `Result` containing the previous callback if successful, or an error code if not.
///
/// Callbacks:
/// - `0`: General callback.
/// - Any other callback: Not supported.
fn subscribe(
&self,
subscribe_number: usize,
callback: Option<Callback>,
process_id: ProcessId,
) -> Result<Callback, (Option<Callback>, ErrorCode)>;
}
Flash Controller Driver Capsule
This module is vendor-specific. The common trait to be implemented is within kernel::hil::flash::Flash.
a6784b6
Image Loading
Overview
The Image Loading module is a component of the MCU Runtime SDK designed for managing SOC images. This module provides APIs for:
- Loading SOC images to target components. The SOC images could come from a flash storage or from another platform capable of streaming images through PLDM T5 (e.g., a BMC Recovery Agent).
- Verifying and authenticating the SOC Images through the Caliptra Core. Images that are loaded to the target SOC components will be authenticated using a mailbox command to the Caliptra core and are verified against the measurements in the SOC Manifest.
The diagram below provides an example of how the Caliptra subsystem, integrated with custom SOC elements (highlighted in green), facilitates the loading of SOC images to vendor components.
Custom SOC elements:
- External Flash : A flash storage containing SOC manifest and the SOC images.
- Vendor CPU: A custom CPU that executes code from a coupled Vendor RAM
- Vendor RAM: RAM exclusively used by the Vendor CPU and is programmable via AXI bus.
- Vendor Cfg Storage: A volatile memory storage used to contain vendor specific configurations.
- SOC Images SOC Image 1 is a firmware for Vendor CPU and loaded to Vendor RAM. SOC Image 2 is a configuration binary to be loaded to Vendor Cfg Storage.
- SOC Config : A register accessible by the MCU ROM to select appropriate source (flash or PLDM) for loading the SOC images.
- Caliptra 'Go' Wire : A signal controlled by the Caliptra core routed to the reset line of the Vendor CPU.
Supported Topologies
Definitions
- Image Component : The binary data (e.g. firmware, configuration) identified uniquely by a component_id.
- Image Location : The location where the image component is loaded (e.g. an instruction or data memory) identified by an image_id. The location should have an associated AXI address.
Image and Instance Mapping
- Each image location will be associated to an image component in the SOC Manifest.
Topology 1: One Image Component per Image Location
In this topology, each image location has its own component. For example, 2 MCUs with their own instruction memories running 2 different firmware images.
flowchart TD
img1a["Image Component (component_id=1)"]
mem1a["Instruction Memory 1 (image_id=1)"]
mcu1a["Vendor MCU 1"]
img1a --> mem1a --> mcu1a
img2["Image Component (component_id=2)"]
mem2["Instruction Memory 2 (image_id=2)"]
mcu2["Vendor MCU 2"]
img2 --> mem2 --> mcu2
Another configuration for this topology is when the component is shared across multiple processing elements.
flowchart TD
img1b["Image Component (component_id=1)"]
mem1b["Instruction Memory 1 (image_id=1)"]
mcu1b["Vendor MCU 1"]
mcu2b["Vendor MCU 2"]
img1b --> mem1b
mem1b --> mcu1b
mem1b --> mcu2b
Topology 2: One Image for multiple instances
flowchart TD %% Image img["Image Component(component_id=1)"] %% Instruction Memories mem1["Instruction Memory 1 (image_id=1)"] mem2["Instruction Memory 2 (image_id=2)"] mem3["Instruction Memory 3 (image_id=3)"] %% Vendor MCUs mcu1["Vendor MCU 1"] mcu2["Vendor MCU 2"] mcu3["Vendor MCU 3"] %% Connections img --> mem1 --> mcu1 img --> mem2 --> mcu2 img --> mem3 --> mcu3
Image Loader supports architectures with a combination of these topologies.
Image Loading Steps
The sequence diagram below shows the high level steps of loading MCU RT image and SOC images.
- Red Arrows indicates actions taken by Caliptra RT
- Purple Arrows indicates actions taken by MCU ROM
- Blue Arrows indicates actions taken by MCU RT
- Black Arrows indicates actions taken by the PLDM FW Update Agent
The following steps are done for every SOC image:
The following outlines the steps carried out by the MCU RT during the SOC boot process:
-
MCU ROM reads a SOC Configuration register (implementation specific) to determine the source of the images to load (Flash/PLDM).
-
Caliptra RT authorizes and loads Caliptra RT (refer to Caliptra Subsystem boot flow for the detailed steps).
-
Caliptra switches to Caliptra RT FW.
-
Caliptra RT indicates to Recovery I/F that it is ready for the SOC manifest image (refer to Caliptra Subsystem Recovery Sequence for the detailed steps).
-
Retrieve SOC Manifest
- If image is coming from PLDM, PLDM FW Update Agent transfers SOC manifest to Recovery I/F
- If Image is coming from Flash, MCU ROM transfers SOC manifest from flash to Recovery I/F
-
Caliptra RT transfers SOC Manifest to Caliptra Mailbox (MB) SRAM
-
Caliptra RT will authenticate its image sitting in Caliptra MB SRAM
-
Caliptra RT indicates to Recovery I/F that it is ready for the next image that should be the MCU RT Image (refer to Caliptra Subsystem Recovery Sequence for the detailed steps)..
-
Retrieve MCU RT Image
- If Image is coming from PLDM, PLDM FW Update Agent sends MCU RT Image to Recovery I/F (refer to Caliptra Subsystem boot flow).
- If Image is coming from Flash, MCU ROM transfers MCU RT Image to Recovery I/F
-
Caliptra RT FW will read the recovery interface registers over AXI manager interface and write the image to MCU SRAM aperture
-
Caliptra RT FW will instruct its SHA accelerator to hash the MCU RT Image in the MCU SRAM.
-
Caliptra RT FW will use this hash and verify it against the hash in the SOC manifest.
-
Once the digest is verified, Caliptra RT FW sets the EXEC/GO bit.
-
The EXEC/GO bit sets a Caliptra wire to MCU (as a consequence of setting the EXEC/GO bit in the previous step). When MCU detects this event, it sets a parameter using the FW HandOff table to indicate the image source (i.e. the image source where it booted from).
-
MCU switches to MCU RT
-
MCU RT retrieves the image source from HandOff table
For every image that needs to be loaded, user initiates a call to load an image identified by an image_id:
- MCU RT application initializes the image loader based on the boot_source. The application need to specify the boot_source as Flash or PLDM.
- Retrieve TOC 18.1 If boot_source = PLDM: 18.1.1 MCU starts the PLDM service. 18.1.2β18.1.3 PLDM Update agent queries device identifiers and receives a response. Device Identifier is vendor specific and should correspond to the device identifier in the PLDM Package in the Update Agent. 18.1.4β18.1.5 PLDM Update agent requests and receives firmware parameters. The PLDM firmware parameter to be used should be for the streaming boot component (refer to the PLDM Package documentation)
| Field Name | Description | Value to Use |
|---|---|---|
ComponentActivationMethods | Defines activation methods supported by the FD. | 0x0000 (Automatic) |
CapabilitiesDuringUpdate | Capabilities of the firmware component during update. | 0x00000002 (Downgrade permitted) |
ActiveComponentVersionString | Describes the currently active version of the component. | None |
PendingComponentVersionString | Describes the version of the component that is pending activation. | None |
ComponentIdentifier | Unique ID for the component. | 0xFFFF |
ComponentClassificationIndex | Used to distinguish identical component classifications with different instances. | 0x00 |
ActiveComponentComparisonStamp | Comparison stamp for active version. | 0x00000000 |
ActiveComponentVersionStringType | String type for active version string. | 0x01 (ASCII) |
ActiveComponentVersionStringLength | Length of the active version string. | 0x00 |
ActiveComponentReleaseDate | Release date of the active component in YYYYMMDD format. | "00000000" |
PendingComponentComparisonStamp | Comparison stamp for pending version. | None |
PendingComponentVersionStringType | String type for pending version string. | 0x01 (ASCII) |
PendingComponentVersionStringLength | Length of the pending version string. | 0x00 |
PendingComponentReleaseDate | Release date of the pending version in YYYYMMDD format. | "00000000" |
ComponentCount | Number of components on the FD. | 0x0001 |
ActiveComponentImageSetVersionStringType | Type for image set version string (active). | 0x01 (ASCII) |
ActiveComponentImageSetVersionStringLength | Length of image set version string (active). | 0x00 |
PendingComponentImageSetVersionStringType | Type for image set version string (pending). | 0x01 (ASCII) |
PendingComponentImageSetVersionStringLength | Length of image set version string (pending). | 0x00 |
ActiveComponentImageSetVersionString | Version string of the active image set. | None |
PendingComponentImageSetVersionString | Version string of the pending image set. | None |
18.1.6β18.1.7 PLDM sends a RequestUpdate, MCU responds with approval. 18.1.8β18.1.9 PLDM sends component information using PassTableComponent request, MCU responds with success. 18.1.10β18.1.11 PLDM sends an UpdateComponent, MCU acknowledges.
18.1.12. Retrieve TOC (Table of Contents) via PLDM starting from offset 0 until the end of TOC 18.1.12.1 MCU send RequestFirmwareData 18.1.12.2 PLDM responds with the TOC chunk
18.2 If boot_source = Flash: 18.2.1 MCU reads the TOC directly from Flash.
For each SOC instance to be loaded and authorized:
19.1 MCU RT initiates to load the an image component for location for image_id=1 by executing image_loader.load_and_authorize(image_id).
19.2β19.3 MCU retrieves the image_info using Caliptra mailbox for the provided image_id. This should return the load_address and the corresponding component_id associated to the image_id.
19.4 MCU determines the image offset and length using the TOC and component_id.
19.5. Transfer image in chunks (from image_offset to image_offset + image_size): 19.5.1.1 If from Flash: Flash sends the image chunk to MCU.
19.5.2.1β19.5.2.2 If from PLDM:
MCU requests firmware data from PLDM.
PLDM responds with the image chunk.
19.5.3 MCU writes the image chunk to the appropriate component destination (e.g., Vendor RAM or Config) identified by the load_address.
19.6 After download is completed, MCU sends an authorize(instance_id) command to Caliptra via Mailbox.
19.7 Caliptra forwards the image to the SHA Accelerator.
19.8 Caliptra verifies the image hash in SHA Acc against the value in the SOC manifest.
19.9 On successful verification, Caliptra returns a success response via the Mailbox to the MCU
20 After all SOC instances' images are loaded, MCU RT application deinitializes the image loader.
21 If boot_source = PLDM, MCU finalizes the PLDM firmware update sequence.
21.1β21.2 MCU sends VerifyComplete, and PLDM Update agent responds.
21.3β21.4 MCU sends ApplyComplete, and PLDM Update agent responds.
21.5β21.6 PLDM Update agent issues an Activate, and MCU confirms.
21.7 MCU stops the PLDM service.
Architecture
The following diagram presents the software stack architecture where the Image Loading module resides.
At the top of the stack, the user application interacts with the Image Loading module through high-level APIs. The user application is responsible for initiating the image loading and verification.
The Image Loading module provides the interface to retrieve and parse the manifest from the flash storage, and transfer SOC images from the storage to the target destination.
Application Interfaces
The APIs are presented as methods of the ImageLoader trait.
#![allow(unused)] fn main() { /// Trait defining the Image Loading module pub trait ImageLoaderAPI { /// Loads the specified SoC image to a storage mapped to the AXI bus memory map. /// /// # Parameters /// image_id: The unsigned integer identifier for the image location /// /// # Returns /// - `Ok()`: Image has been loaded and authorized succesfully. /// - `Err(ErrorCode)`: Indication of the failure to load or authorize the image. async fn load_and_authorize(&self, image_id: u32) -> Result<(), ErrorCode>; /// Releases any resources held by ImageLoader /// Finalizes the PLDM Update and stops the PLDM service if started async fn finalize(); } /// ImageLoader Implementation pub struct ImageLoader { pub fn new(boot_source: ImageSource) -> Result<(), ErrorCode> { // if boot_source = Flash, read TOC from flash // if boot_source = PLDM, start PLDM service, and download TOC from offset 0 } } impl ImageLoaderAPI for ImageLoader { // API Implementation } pub enum ImageSource { // Image is located in Flash Flash, // Image is retrieved via PLDM // The PLDM DeviceID to be used should be specified Pldm(DeviceId), } Example Usage: /// Load image for instances 1 and 2 from flash let image_loader = ImageLoader::new(ImageSource::Flash)?; image_loader.load_and_authorize(1).await?; image_loader.load_and_authorize(2).await?; image_loader.finalize(); /// Load image for instances 3 and 4 from PLDM let image_loader = ImageLoader::new(ImageSource::Pldm(DEVICE_ID))?; image_loader.load_and_authorize(3).await?; image_loader.load_and_authorize(4).await?; image_loader.finalize(); }
Recovery Boot Flow
During system initialization, the recovery boot flow ensures that valid firmware is successfully loaded into the Caliptra core, MCU, and other SoC elements.
In flash-based systems, an A/B partitioning mechanism is used to enhance reliability by allowing fallback to a working firmware image in case of boot failure.
The method of selecting which partition to boot from is system-specific. For example, the active partition may be determined by a hardware pin, a soft fuse or register, or an entry in the flash partition table.
Caliptra Boot Flow
This section describes the actions taken by the Caliptra ROM and Caliptra FMC/Runtime (RT) during boot, particularly in handling boot failures.
If a corrupted or unauthorized Caliptra FMC+RT image is downloaded, the Caliptra ROM sets the CPTRA_FW_ERROR_FATAL register. MCU then initiates a recovery mechanism to provide the Caliptra core with a valid firmware image on the next SOC reset.
If the firmware image fails to execute properly (e.g., hangs), the Caliptra watchdog triggers a timeout, causing an error to be asserted to the MCU ROM, which then initiates the recovery process.
flowchart TD
CaliptraFlow([Start]) -->Start
Start[Execute Caliptra ROM] --> StartWD[Start Watchdog]
StartWD --> SetReady[Set RECOVERY_STATUS = Awaiting Image]
SetReady -->|Recovery Image Available| DownloadFMC[Stream FMC + RT from Recovery Interface]
DownloadFMC --> AuthFMC[Authenticate FMC + RT]
AuthFMC -->|Auth Pass| BootRuntime[Execute FMC + RT]
AuthFMC -->|Auth Fail| ClearSignals[Set RECOVERY_STATUS = AuthFailed]
ClearSignals -->|Warm Boot| NonFatal[Set cptra_error_non_fatal]
ClearSignals -->|Cold Boot| Fatal[Set cptra_error_fatal]
NonFatal --> WaitReset[Wait for SOC Reset] -->|Reset| Start
Fatal --> WaitReset
BootRuntime --> LoadManifest[Load & <br>Auth SoC Manifest]
LoadManifest -->|Auth Pass| LoadMCURT[Load & Auth MCU RT]
LoadMCURT -->|Auth Pass| StartMCURT[Set Exec/Go for MCU<br>Set RECOVERY_STATUS = 'Recovery successful']
LoadMCURT -->|Auth Fail| ClearSignals
CPTRA_WD_TIMEOUT[Watchdog Timeout] --> Fatal
StartMCURT --> Done_cptra([Done])
MCU ROM Boot Flow
This section describes how the MCU ROM handles failures during the loading of Caliptra firmware and MCU Runtime firmware.
To avoid infinite reboot loops caused by a faulty firmware image, each partition maintains a boot count, which tracks how many times the system has attempted to boot from it.
On power-up, the MCU ROM checks the partition table for the status and boot count of the currently active partition.
If the count is below a predefined threshold (e.g., MAX_COUNT), the system increments the count and attempts to boot the partition.
If the count exceeds MAX_COUNT, the partition is marked as unhealthy, and the MCU triggers a recovery flow, switching to another valid partition if available.
After a successful boot, the partition status is marked as "Boot Successful". Future boots may reset the count or ignore it.
This mechanism ensures automatic rollback to a working firmware without manual intervention.
flowchart TD
MCUFlow([Start])-->MCUROM
TIMEOUT([Watchdog<br>Timeout])-->MCUROM
MCUROM[Execute MCU ROM]
MCUROM --> StartMCUWD[Start MCU<br>Watchdog]-->
READ_PART[Retrieve Active<br>Partition]-->CHECK_BOOT_COUNT
MonitorErrors[Monitor Caliptra<br>Errors] --> |cptra_error_non_fatal<br>cptra_error_fatal| Recover
CHECK_BOOT_COUNT[Check active partition<br>boot count<br>and status] --> |Status =='Valid'<br>&&<br>Boot count >= MAX_COUNT| Recover
CHECK_BOOT_COUNT --> |Status =='Boot Successful'<br>i.e. partition booted before| CHECK_RESET_REASON
CHECK_BOOT_COUNT --> |Status='Valid'<br>&&<br>Boot count < MAX_COUNT| INCREMENT_COUNT[Increment Boot count] --> CHECK_RESET_REASON
CHECK_RESET_REASON[Get RESET_REASON]-->|Cold Boot|LoadFMC[Load Caliptra<br>FMC + Runtime<br>from Active Flash<br>Partition]
CHECK_RESET_REASON-->|Warm Boot|LoadMCURTWarm[Load MCU RT<br>from Active Partition]-->AuthorizeMCURT[Authorize MCU RT]
AuthorizeMCURT-->|Auth Pass|MCURTStart
AuthorizeMCURT-->|Auth Fail|Recover
LoadFMC --> WaitRecoveryReady[Wait for<br>RECOVERY_STATUS<br>to be 'Awaiting<br>Image']
--> |RECOVERY_STATUS =<br>'Awaiting Image'| StreamFMC[Stream Caliptra<br>FMC + Runtime<br>to Recovery Interface]
StreamFMC --> WaitForCaliptraBoot[Wait Caliptra<br>RT Boot]
WaitForCaliptraBoot --> |Caliptra RT Booted,<br>RECOVERY_STATUS =<br>'Recovery successful'| StreamManifest[Load Manifest<br>from Active<br>partition and<br>stream to Recovery<br>Interface]
--> StreamMCURT[Load MCU RT<br>from Active<br>partition and<br>stream to Recovery<br>Interface]
StreamMCURT--> WaitMCURTBoot[Wait for MCU<br>Exec/Go Bit<br>to be set]
WaitMCURTBoot-->|Exec Go/Bit<br>set|MCURTStart([MCU Runtime Boot Flow])
Recover([MCU Recovery Flow])
MCU Recovery Flow
When a boot failure occurs (e.g., due to exceeding maximum boot attempts, authentication failure, or watchdog timeout), the MCU Recovery Flow is triggered to restore system operability by switching to a known-good firmware partition.
If the active partition fails to boot, the system attempts to revert to the other available and valid partition.
The MCU will indicate to the SoC that an error occured and will need a full SOC Reset.
flowchart TD
Start([MCU Recovery Flow])--> InvalidateActivePartition[Set Active<br>Partition as<br>Invalid] --> SetStatusBootFailed[Set Partition<br>Status as Boot Failed]
--> SetNewActivePartition[Set other valid<br>partition, if any,<br>as Active]
--> SetFatalError[Set FW_ERROR_FATAL<br> MCI Reg]
--> WaitForSOCReset[Wait for<br>SOC Reset]
MCU Runtime and SoC Image(s) Boot Flow
When the MCU ROM hands off execution to the MCU Runtime firmware, it keeps the MCU watchdog timer active. If the watchdog expires at any pointβindicating a hang or prolonged stallβthe MCU will be reset. Upon reset, the MCU ROM will evaluate the boot count and initiate the recovery flow if necessary (e.g., if the boot count exceeds the defined threshold).
The MCU Runtime firmware is responsible for loading and authorizing the remaining SoC images. If all SoC images are successfully loaded, authenticated, and booted, the partition's boot status can be marked as "Boot Successful." However, if any of the images fail to load or validate, the system behavior is left to user-defined policy. For example, the user may choose to trigger the recovery flow to fall back to a previous version of the SoC images or halt execution entirely to prevent further operation under an unstable configuration.
flowchart TD
Start([MCU Runtime<br>Boot Flow])-->MCURuntime
MCURuntime[Execute MCU<br>Runtime FW]
MCURuntime-->StartWatchdog[Start MCU<br>Watchdog]
StartWatchdog-->LoadAndAuthorizeSocImage[Load and<br>Authorize SOC<br>Images]
LoadAndAuthorizeSocImage --> |Auth Pass| SUCCESSFUL_BOOT[Set Partition <br>Status as <br>'Boot Successful'] --> Done([Done])
LoadAndAuthorizeSocImage --> |Auth Fail| UserDefinedOption[User Defined<br>Logic - e.g.<br>Switch to other<br>partition or Hang]
TIMEOUT([MCU Watchdog<br>Timeout])-->MCURESET[MCU Reset]-->MCUROM([MCU ROM])
References:
- OCP Recovery Interface [https://www.opencompute.org/documents/ocp-recovery-document-1p0-final-1-pdf]
- Caliptra Error and Recovery Flow [https://github.com/chipsalliance/caliptra-rtl/blob/main/docs/CaliptraIntegrationSpecification.md#caliptra-error-and-recovery-flow]
- Fatal and Non-Fatal Errors [https://github.com/chipsalliance/Caliptra/blob/main/doc/Caliptra.md#error-reporting-and-handling]
a6784b6
MCTP Stack
The Caliptra subsystem supports SPDM, PLDM, and Caliptra vendor-defined message protocols over MCTP.
The MCTP base protocol is implemented as a Tock Capsule, which also handles the essential MCTP Control messages. Additionally, it offers a syscall interface to userspace, enabling the sending and receiving of MCTP messages for other supported protocols. Caliptra MCTP endpoint has only one EID and supports dynamic assignment by the MCTP bus owner.
MCTP Packets are delivered over physical I3C medium using I3C transfers. Caliptra MCTP endpoint always plays the role of I3C Target and is managed by an external I3C controller. Minimum transmission size is based on the MCTP baseline MTU (for I3C it is 69 bytes: 64 bytes MCTP payload + 4 bytes MCTP header + 1 byte PEC). Larger than the baseline transfer may be possible after discovery and negotiation with the I3C controller. The negotiated MTU size will be queried from the I3C Target peripheral driver by MCTP capsule.
MCTP Send Sequence
sequenceDiagram
participant Application
participant VirtualMCTPDriver
participant MuxMCTPDriver
participant MCTPI3CBinding
participant I3CTarget
participant I3CController
Application--)VirtualMCTPDriver: Send request/response <br/>message to a destination EID
VirtualMCTPDriver->>VirtualMCTPDriver: mctp_sender.send_message. <br/>Sets the mctp_sender context.
VirtualMCTPDriver->> MuxMCTPDriver: mctp_mux_sender.send() <br/> Adds mctp_sender to the tail of sender_list.
loop Packetize the entire message payload
MuxMCTPDriver->>MuxMCTPDriver: Add MCTP Transport header.
MuxMCTPDriver->>MCTPI3CBinding: transmit() MCTP packet
MCTPI3CBinding->>MCTPI3CBinding: Compute PEC and add <br/>to the end of the packet.
MCTPI3CBinding->>I3CTarget: transmit() MCTP packet with PEC
alt IBI mode enabled
I3CTarget->>I3CController: IBI with MDB
I3CController--)I3CTarget: Private Read Request
I3CTarget--)I3CController: MCTP packet
I3CTarget->>I3CTarget: result = SUCCESS
else Polling mode
I3CTarget->>I3CTarget: 1. Set the pending read bit. <br/> 2. Set the alarm for tx_timeout time
alt Controller sent GETSTATUS CCC
I3CTarget--)I3CController: Report nonzero value
I3CController--)I3CTarget: Private Read Request
I3CTarget--)I3CController: MCTP packet
I3CTarget->>I3CTarget: set result = SUCCESS
else alarm fired
I3CTarget->>I3CTarget: set result = TIMEOUT
end
end
I3CTarget->>MCTPI3CBinding: send_done() with result. <br/>Return packet buffer.
MCTPI3CBinding->>MuxMCTPDriver: send_done() with result.<br/> Return packet buffer.
end
The send stack is as shown in the picture below:
MCTP Receive sequence
sequenceDiagram
participant I3CController
participant I3CTarget
participant MCTPI3CBinding
participant MuxMCTPDriver
participant VirtualMCTPDriver
participant Application
loop Assemble packets until eom
I3CController--)I3CTarget: I3C Private Write transfer
I3CTarget->>MCTPI3CBinding: if no rx buffer, call write_expected() callback
MCTPI3CBinding->> MuxMCTPDriver: write_expected() callback
MuxMCTPDriver->>MCTPI3CBinding: set_rx_buffer() with buffer to receive packet
MCTPI3CBinding->> I3CTarget: set_rx_buffer() with buffer to receive the packet
I3CTarget--) I3CController : Send ACK
I3CController--)I3CTarget: MCTP packet
Note over I3CController, I3CTarget: Receive entire MCTP packet <br/>including the PEC until Sr/P.
I3CTarget->> MCTPI3CBinding: receive() to receive the packet
MCTPI3CBinding ->> MCTPI3CBinding: Check the PEC, and pass the packet <br/>with MCTPHeader to Mux MCTP layer
MCTPI3CBinding->>MuxMCTPDriver: receive() to receive the packet
MuxMCTPDriver->>MuxMCTPDriver: Process MCTP transport header on packet, <br/> and assemble if matches any pending Rx states<br/>or handle MCTP control msg.
end
MuxMCTPDriver->>VirtualMCTPDriver: receive() call to receive the assembled message.
VirtualMCTPDriver--)Application: Schedule upcall to receive the request/response.
(The receive stack picture is nearly identical to the send stack above.)picture below:
Syscall Library in userspace
Userspace applications can use syscall library in to send and receive MCTP messages. The following APIs are provided by the MCTP syscall library.
Each user space application will instantiate the Mctp module with appropriate driver number. The Mctp module provides the following APIs to send and receive MCTP messages.
//! The MCTP library provides the interface to send and receive MCTP messages.
//! The MCTP library is implemented as an async library to allow the userspace application to send and receive MCTP messages asynchronously.
//!
//! Usage
//! -----
//!```Rust
//! use mctp::Mctp;
//!
//! const SPDM_MESSAGE_TYPE: u8 = 0x5;
//! const SECURE_SPDM_MESSAGE_TYPE: u8 = 0x6;
//!
//! #[embassy_executor::task]
//! async fn async_main() {
//! /// Initialize the MCTP driver with the driver number
//! let mctp = Mctp::<TockSyscalls>::new(MCTP_SPDM_DRIVER_NUM);
//!
//! loop {
//! /// Receive the MCTP request
//! let mut rx_buf = [0; 1024];
//! let res = mctp.receive_request(&mut rx_buf).await;
//! match res {
//! Ok((req_len, msg_info)) => {
//! /// Process the received message
//! /// ........
//! /// Send the response message
//! let mut tx_buf = [0; 1024];
//! let result = mctp.send_response(&tx_buf, msg_info).await;
//! match result {
//! Ok(_) => {
//! /// Handle the send response success
//! }
//! Err(e) => {
//! /// Handle the send response error
//! }
//! }
//! }
//! Err(e) => {
//! /// Handle the receive request error
//! }
//! }
//! }
//! }
//!```
/// mctp/src/lib.rs
type EndpointId = u8;
type Tag = u8;
pub struct MessageInfo {
eid: EndpointId,
tag: Tag,
}
pub struct Mctp<S: Syscalls> {
syscall: PhantomData<S>,
driver_num: u32,
}
impl<S: Syscalls> Mctp<S> {
/// Create a new instance of the MCTP driver
///
/// # Arguments
/// * `driver_num` - The driver number for the MCTP driver
///
/// # Returns
/// * `Mctp` - The MCTP driver instance
pub fn new(drv_num: u32) -> Self;
/// Check if the MCTP driver for a specific message type exists
///
/// # Returns
/// * `bool` - `true` if the driver exists, `false` otherwise
pub fn exists() -> Result<(), ErrorCode>;
/// Receive the MCTP request.
/// Receives a message from any source EID. The user should use the returned MessageInfo to send a response.
///
/// # Arguments
/// * `req` - The buffer to store the received request payload
///
/// # Returns
/// * `(u32, MessageInfo)` - On success, returns tuple containing length of the request received and the message information containing the source EID, message tag
/// * `ErrorCode` - The error code on failure
pub async fn receive_request(&self, req: &mut [u8]) -> Result<(u32, MessageInfo), ErrorCode>;
/// Send the MCTP response to an endpoint
///
/// # Arguments
/// * `resp` - The buffer containing the response payload
/// * `info` - The message information containing the destination EID, message tag which was received in `receive_request` call
///
/// # Returns
/// * `()` - On success
/// * `ErrorCode` - The error code on failure
pub async fn send_response(&self, resp: &[u8], info: MessageInfo) -> Result<(), ErrorCode>;
/// Send the MCTP request to the destination EID
/// The function returns the message tag assigned to the request by the MCTP Capsule.
/// This tag will be used in the `receive_response` call to receive the corresponding response.
///
/// # Arguments
/// * `dest_eid` - The destination EID to which the request is to be sent
/// * `req` - The payload to be sent in the request
///
/// # Returns
/// * `Tag` - The message tag assigned to the request
/// * `ErrorCode` - The error code on failure
pub async fn send_request(&self, dest_eid: u8, req: &[u8]) -> Result<Tag, ErrorCode>;
/// Receive the MCTP response from an endpoint
///
/// # Arguments
/// * `resp` - The buffer to store the received response payload from the endpoint
/// * `tag` - The message tag to match against the response message
///
/// # Returns
/// * `(u32, MessageInfo)` - On success, returns tuple containing length of the response received and the message information containing the source EID, message tag
/// * `ErrorCode` - The error code on failure
pub async fn receive_response(&self, resp: &mut [u8], tag: Tag) -> Result<(u32, MessageInfo), ErrorCode>;
}
Userspace Driver and Virtualizer layer
During the board initialization, three separate instances of the virtual MCTP driver are created, each assigned a unique driver number. These instances correspond to the SPDM, PLDM, and Vendor Defined message types. Each driver instance is designed to communicate directly with its corresponding protocol application.
/// define custom driver numbers for Caliptra
pub enum NUM {
...
// Mctp
MctpSpdm = 0xA0000,
MctpSecureSpdm = 0xA0001,
MctpPldm = 0xA0002,
MctpVenDef = 0xA0003,
...
}
/// mctp/driver.rs
pub const MCTP_SPDM_DRIVER_NUM: usize = driver::NUM::MctpSpdm;
pub const MCTP_PLDM_DRIVER_NUM: usize = driver::NUM::MctpPldm;
pub const MCTP_VENDEF_DRIVER_NUM: usize = driver::NUM::MctpVenDef;
Syscalls provided
Virtual MCTP driver provides system calls to interact with the userspace application. This layer implements the SyscallDriver trait.
The following are the list of system calls provided by the MCTP Capsule.
-
Read-Write Allow
- Allow number: 0
- Description: Used to set up the Rx buffer for receiving the MCTP Request/Response message payload.
- Argument: Slice into which the received MCTP Request/Response message should be stored.
- Allow number: 0
-
Read-Only Allow
- Allow number: 0
- Description: Used to set up the Tx buffer for sending the MCTP Request/Response message payload.
- Argument: Slice containing the MCTP message payload to be transmitted.
- Allow number: 0
-
Subscribe
- Subscribe number 0:
- Description: Callback when message is received.
- Argument 1: The callback
- Argument 2: App specific data
- Subscribe number 1:
- Description: Callback when message is transmitted.
- Argument 1: The callback
- Argument 2: App specific data
- Subscribe number 0:
-
Command
- Command number 0:
- Description: Existence check
- Command number 1:
- Description: Receive Request
- Argument1 : Source EID
- Argument2 : Message Tag
- Command number 2:
- Description: Receive Response
- Argument1 : Source EID
- Argument2 : Message Tag
- Command number 3:
- Description: Send Request
- Argument1 : Destination EID
- Argument2 : Message Tag
- Command number 4:
- Description: Send Response
- Argument1 : Destination EID
- Argument2 : Message Tag
- Command number 0:
Virtualized Layer
MCTP capsule stores the following process context specific information in the Process's grant region.
enum OperationType {
Tx,
Rx,
Idle
}
struct OperationCtx {
msg_tag : u8,
peer_eid : u8,
is_busy: bool,
op_type:OperationType,
}
#[derive(default)]
pub struct App {
pending_rx: OperationCtx,
bound_msg_type : u8,
}
/// Implements userspace driver for a particular msg_type.
pub struct VirtualMCTPDriver {
mctp_sender: &dyn MCTPSender,
apps : Grant<App, 2 /*upcalls*/, 1 /*allowro*/, 1/*allow_rw*/>,
app_id: Cell<Option<ProcessID>>,
msg_types: [u8],
kernel_msg_buffer: MapCell<SubSliceMut<'static, u8>>,
}
MCTP Send state
/// The trait that provides an interface to send the MCTP messages to MCTP kernel stack.
pub trait MCTPSender {
/// Sets the client for the `MCTPSender` instance.
/// In this case it is MCTPTxState which is instantiated at the time of
fn set_client(&self, client: &dyn MCTPTxClient);
/// Sends the message to the MCTP kernel stack.
fn send_msg(&self, dest_eid: u8, msg_tag: u8, msg_payload: SubSliceMut<'static, u8>) -> Result<(), SubSliceMut<'static, u8>>;
}
/// This is the trait implemented by VirtualMCTPDriver instance to get notified after
/// message is sent.
/// The 'send_done' function in this trait is invoked after the MCTPSender
/// has completed sending the requested message.
pub trait MCTPTxClient {
fn send_done(&self, msg_tag: Option<u8>, result: Result<(), ErrorCode>, msg_payload: SubSliceMut<'static, u8> )
}
pub struct MCTPTxState<M:MCTPTransportBinding> {
/// MCTP Mux driver reference
mctp_mux_sender: &MuxMCTPDriver<M>,
/// Client to invoke when send done. This is set to the corresponding VirtualMCTPDriver instance.
client: OptionalCell<&dyn MCTPTxClient>,
/// next MCTPTxState node in the list
next: ListLink<MCTPTxState<M: MCTPTransportBinding>>,
/// The message buffer is set by the virtual MCTP driver when it issues the Tx request.
msg_payload: MapCell<SubSliceMut<'static, u8>>,
}
MCTP Receive state
/// This is the trait implemented by VirtualMCTPDriver instance to get notified of
/// the messages received on corresponding msg_type.
pub trait MCTPRxClient {
fn receive(&self, dst_eid: u8, msg_type: u8, msg_tag: u8, msg_payload: &[u8]);
}
/// Receive state
pub struct MCTPRxState {
/// Client (implements the MCTPRxClient trait)
client: OptionalCell<&dyn MCTPRxClient>,
/// static Message buffer
msg_payload: MapCell<'static, [u8]>,
/// next MCTPRxState node
next: ListLink<MCTPRxState>,
}
MCTP Mux Layer
The MCTP Mux layer acts as the sole Tx client to the rest of the MCTP stack. The MuxMCTPDriver struct contains a list of statically allocated sender structs that implement the MCTPSender trait. This struct provides methods to packetize the message of the inflight (popped from head of the list) send request.
The MCTP Mux layer also contains a list of statically allocated receiver structs that implement the MCTPRxClient trait. This struct provides methods to assemble the received packets into a complete message.
If the message originates from the device (with msg_tag = 0x8), a new msg_tag will be allocated and provided to the client via the send_done callback. This msg_tag is passed to the application layer which uses it to issue the receive response command.
For response messages, where msg_tag values range between 0 and 7, the same value is used to encapsulate the MCTP transport header on each packet.
MCTP Mux layer is the single receive client for the MCTP Device Layer. This layer is instantiated with a single contiguous buffer for Rx packet of size kernel::hil:i3c::MAX_TRANSMISSION_UNIT.
The Rx buffer is provided to the I3C target driver layer to receive the packets when the I3C controller initiates a private write transfer to the I3C Target.
The virtualized upper layer ensures that only one message is transmitted per driver instance at a time. Receive is event based. The received packet in the Rx buffer is matched against the pending receive requests by the use
/// The MUX struct manages multiple MCTP driver users (clients).
pub struct MuxMCTPDriver<M: MCTPTransportBinding> {
/// Reference to the MCTP transport binding layer that implements the MCTPTransportBinding trait.
mctp_device: &dyn M,
/// Global message tag. Increment by 1 for next tag up to 7 and wrap around.
next_msg_tag: u8,
/// Local EID assigned to the MCTP endpoint.
local_eid: u8,
/// Maximum transmission unit (MTU) size.
mtu: u8,
/// List of outstanding send requests
sender_list: List<MCTPTxState<M>>,
/// List of outstanding receive requests
receiver_list: List<MCTPRxState>,
/// Static buffer for tx packet. (may not be needed)
tx_pkt_buffer: MapCell<SubSliceMut<'static, u8>>,
/// Static buffer for rx packet
rx_pkt_buffer: MapCell<SubSliceMut<'static, u8>>,
}
MCTP Transport binding layer
The following is the generic interface for the MCTP physical transport binding layer. Implementer of this trait will add physical medium specific header/trailer to the MCTP packet.
/// This trait contains the interface definition
/// for sending the MCTP packet through MCTP transport binding layer.
pub trait MCTPTransportBinding {
/// Set the client that will be called when the packet is transmitted.
fn set_tx_client(&self, client: &TxClient);
/// Set the client that will be called when the packet is received.
fn set_rx_client(&self, client: &RxClient);
/// Set the buffer that will be used for receiving packets.
fn set_rx_buffer(&self, rx_buf: &'static mut [u8]);
fn transmit(&self, tx_buffer: &'static mut [u8]);
/// Enable/Disable the I3C target device
fn enable();
fn disable();
/// Get the maximum transmission unit (MTU) size.
fn get_mtu_size() -> usize;
}
MCTP I3C Transport binding layer is responsible for checking the PEC for received packets and adding the PEC for transmitted packets over the I3C medium. It is mostly a passthrough for the MCTP Base layer except, it will need the I3C target device address for PEC calculation.
This layer is also a sole Rx and Tx client for the I3C Target device driver.
pub struct MCTPI3CBinding {
/// Reference to the I3C Target device driver.
mctp_i3c : &dyn I3CTarget,
rx_client: OptionCell<&dyn RxClient>,
tx_client: OptionCell<&dyn TxClient>,
/// I3C Target device address needed for PEC calculation.
device_address: u8,
}
HIL for I3C Target Device
The following trait defined standard and shared interface for I3C Target hardware driver.
/// hil/i3c.rs
pub trait TxClient {
/// Called when the packet has been transmitted. (Calls this after the ACK is received from Controller)
fn send_done(&self,
tx_buffer: &'static mut [u8],
acked: bool,
result : Result<(), ErrorCode>);
}
pub trait RxClient {
/// Called when a complete MCTP packet is received and ready to be processed.
fn receive(&self, rx_buffer: &'static mut [u8], len : usize);
/// Called when the I3C Controller has requested a private Write by addressing the target
/// and the driver needs buffer to receive the data.
/// The client should call set_rx_buffer() to set the buffer.
fn write_expected(&self);
}
pub trait I3CTarget {
/// Set the client that will be called when the packet is transmitted.
fn set_tx_client(&self, client: &TxClient);
/// Set the client that will be called when the packet is received.
fn set_rx_client(&self, client: &RxClient);
/// Set the buffer that will be used for receiving packets.
fn set_rx_buffer(&self, rx_buf: &'static mut [u8]);
I
/// Transmit a packet.
fn transmit(&self, tx_buf: &'static mut[u8], len : usize) -> Result<(), (ErrorCode, &'static mut [u8])>;
/// Enable/disable the I3C target device
fn enable();
fn disable();
/// Get the maximum transmission unit (MTU) size.
fn get_mtu_size() -> usize;
/// Get the address of the I3C target device. Needed for PEC calculation.
fn get_address() -> u8;
}
a6784b6
SPDM
The Security Protocol and Data Model (SPDM) is a protocol designed to ensure secure communication between hardware components by focusing on mutual authentication and the establishment of secure channels over potentially insecure media. SPDM enables devices to verify each other's identities and configurations, leveraging X.509v3 certificates to ensure cryptographic security. Designed for interoperability, it can work across various transport and physical media, often utilizing the Management Component Transport Protocol (MCTP). This protocol is especially valuable in environments where secure hardware communication is crucial, such as data centers and enterprise systems.
Specifications
| Specification | Document Link |
|---|---|
| Security Protocol and Data Model | DSP0274 |
| Secured Messages using SPDM | DSP0277 |
| SPDM over MCTP Binding Specification | DSP0275 |
| Secured Messages using SPDM over MCTP Binding | DSP0276 |
SPDM Protocol Sequence
sequenceDiagram
participant Requester
participant Responder
Requester->>Responder: GetVersion
Responder-->>Requester: Version
Requester->>Responder: GetCapabilities
Responder-->>Requester: Capabilities
Requester->>Responder: NegotiateAlgorithms
Responder-->>Requester: Algorithms
opt If supported
Requester->>Responder: GetDigests
Responder-->>Requester: Digests
end
opt If needed
Requester->>Responder: GetCertificate
Responder-->>Requester: Certificate
end
opt If supported
Requester->>Responder: Challenge
Responder-->>Requester: ChallengeAuth
end
opt If supported
Requester->>Responder: GetMeasurements
Responder-->>Requester: Measurements
end
opt If supported
Requester->>Responder: KeyExchange
Responder-->>Requester: KeyExchangeRsp
end
rect rgb(255, 255, 204)
note right of Requester: Secure Session
opt If supported
Requester->>Responder: Finish
Responder-->>Requester: FinishRsp
end
end
Class Diagram
classDiagram
direction RL
MCTP Transport <|--|> SPDMResponder: processRequest() / sendResponse()
SecureSessionMgr <|-- SPDMResponder: processSecureMessage()
TranscriptMgr <|-- SPDMResponder
TranscriptMgr <|-- SecureSessionMgr
class SPDMResponder{
- transcriptMgr
- sessionMgr
+ processRequest()
+ sendResponse()
}
class SecureSessionMgr {
-transcriptMgr
+TraitMethods
}
class TranscriptMgr{
+TraitMethods
}
SPDM Responder
The Responder is responsible for receiving and processing requests from the Requestor. It authenticates the Requestor's identity, attests its own state and configuration, and establishes a secure communication channel. The Responder uses cryptographic techniques, such as validating X.509v3 certificates, to ensure the integrity and confidentiality of the exchanged data.
Responder supported messages
The SPDM Responder supports the following messages:
| Message | Description |
|---|---|
VERSION | Retrieves version information |
CAPABILITIES | Retrieves SPDM capabilities |
ALGORITHMS | Retrieves the negotiated algorithms |
DIGESTS | Retrieves digest of the certificate chains |
CERTIFICATE | Retrieves certificate chains |
MEASUREMENTS | Retrieves measurements of elements such as intenral state |
KEY_EXCHANGE_RSP | Retrieves the responder's public key information |
FINISH_RSP | Provide key confirmation, bind the identity of each party to the exchanged keys |
END_SESSION_ACK | End session acknowledgment |
ERROR | Error message |
Responder Interface
pub struct SpdmResponder<T: MctpTransport, U: SpdmTranscriptManager, V: SpdmSecureSessionManager> {
transport: T,
transcript_manager: U,
session_manager: V,
data_buffer: [u8; MAX_SPDM_MESSAGE_SIZE],
}
impl<T: MctpTransport, U: SpdmTranscriptManager, V: SpdmSecureSessionManager> SpdmResponder<T, U, V> {
pub fn new(transport: T, transcript_manager: U, session_manager: V) -> Self {
SpdmResponder {
transport,
transcript_manager,
session_manager,
data_buffer: [0; MAX_SPDM_MESSAGE_SIZE],
}
}
pub async fn handle_request(&mut self, request_info: u32) {
// request_info: Bits[16:23] Message Type [SPDM | Secure SPDM]
}
}
Transcript Manager
The Transcript Manager is for managing the transcript and the transcript hash. The transcript is a sequential concatenation of prescribed full messages or message fields. The transcript hash is the cryptographic hash of this transcript, computed using the negotiated hash algorithm. This component ensures the integrity and authenticity of SPDM communications by managing these essential elements.
Transcript Manager Interface
pub trait SpdmTranscriptManager {
/// Set the hash algorithm. The algorithm can be set only once.
///
/// # Parameters
/// - `hash_algo`: Hash algorithm to set.
///
/// # Returns
/// - `Result<(), SpdmError>`: Returns `Ok(())` if the hash algorithm was set, or an error code.
fn set_hash_algo(&self, hash_algo: HashType) -> Result<(), SpdmError>;
/// Set the SPDM negotiated version to be used for communication.
///
/// # Parameters
/// - `spdm_version`: SPDM negotiated version.
fn set_spdm_version(&self, spdm_version: u8);
/// Update the transcript with a message.
///
/// # Parameters
/// - `context_type`: Transcript context to update.
/// - `message`: Message to add to the transcript.
/// - `use_session_context`: Use session context to update an SPDM session transcript.
/// - `session_idx`: SPDM session index.
///
/// # Returns
/// - `Result<(), SpdmError>`:
/// Returns `Ok(())` if the message was added to the transcript successfully, or an error code.
async fn update(
&self,
context_type: SpdmTranscriptManagerContextType, // [VCA, M1M2, L1L2, TH]
message: &[u8],
use_session_context: bool,
session_idx: u8,
) -> Result<(), SpdmError>;
/// Get the hash based on the hash type. The hashing operation is finished if `finish_hash` is set
/// to `true`. In that case, an additional call to update will start a new hashing operation.
/// If `finish_hash` is set to `false`, the hash is not finalized and can be updated with additional
/// calls to update.
///
/// # Parameters
/// - `context_type`: Transcript context type to get the hash from.
/// - `finish_hash`: Flag to indicate to finish the hash.
/// - `use_session_context`: Use session context to update an SPDM session transcript.
/// - `session_idx`: SPDM session index.
/// - `hash`: Buffer to copy the hash to.
///
/// # Returns
/// - `Result<Vec<u8>, SpdmError>`: Returns the hash if the operation was successful, or an error code.
fn get_hash(
&self,
context_type: SpdmTranscriptManagerContextType, // [VCA, M1M2, L1L2, TH]
finish_hash: bool,
use_session_context: bool,
session_idx: u8,
hash: &mut [u8]
) -> Result<(), SpdmError>;
/// Reset a transcript context.
///
/// # Parameters
/// - `context_type`: Transcript context to reset.
/// - `use_session_context`: Use session context to update an SPDM session transcript.
/// - `session_idx`: SPDM session index.
fn reset_transcript(
&self,
context_type: SpdmTranscriptManagerContextType, // [VCA, M1M2, L1L2, TH]
use_session_context: bool,
session_idx: u8,
);
}
SPDM Secure Session Manager
The SPDM Secure Session Manager is responsible for managing secure sessions within the SPDM protocol framework. It provides mechanisms to create, release, and retrieve secure sessions. The manager can set and query the state of a session, ensuring secure communication between devices. It generates necessary cryptographic keys, including shared secrets, handshake keys, and data keys, through asynchronous methods. Additionally, it verifies the integrity and optionally decrypts secure messages, and encodes messages with appropriate security measures. The manager also tracks session validity and can reset session states and identifiers as needed, ensuring robust and secure session management.
Secure Session Manager Interface
pub trait SpdmSecureSessionManager {
/// Create a new SPDM secure session.
///
/// # Parameters
/// - `session_id`: Session Id for the session.
/// - `is_requester`: True if the session is for the requester, false otherwise.
/// - `connection_info`: SPDM connection info.
///
/// # Returns
/// - `Option<&SpdmSecureSession>`:
/// A pointer to the created SPDM secure session or `None` if the session could not be created.
fn create_session(
&self,
session_id: u32,
is_requester: bool,
connection_info: &SpdmConnectionInfo,
) -> Result<&SpdmSecureSession, SpdmError>;
/// Release an SPDM secure session.
///
/// # Parameters
/// - `session_id`: Session Id for the session.
fn release_session(&self, session_id: u32);
/// Get an SPDM secure session.
///
/// # Parameters
/// - `session_id`: Session Id for the session.
///
/// # Returns
/// - `Option<&SpdmSecureSession>`: A pointer to the SPDM secure session or `None` if the session does not exist.
fn get_session(& self, session_id: u32) -> Option<& SpdmSecureSession>;
/// Set the session state for an SPDM secure session.
///
/// # Parameters
/// - `session_id`: Session Id for the session.
/// - `session_state`: Session state to set.
fn set_session_state(&self, session_id: u32, session_state: SpdmSecureSessionState);
/// Reset the Session Manager.
fn reset(&self);
/// Generate the shared secret from peer and local public keys.
///
/// # Parameters
/// - `session`: SPDM session info.
/// - `peer_pub_key_point`: Peer public key in point format.
/// - `local_public_key`: Generated local public key in point format on return.
///
/// # Returns
/// - `Result<(), SpdmError>`:
/// Returns the local public key in point format if the shared secret is generated successfully, or an error code.
fn generate_shared_secret(
&self,
session: &SpdmSecureSession,
peer_pub_key_point: &EccPointPublicKey,
local_public_key: &mut [u8]
) -> Result<(), SpdmError>;
/// Generate handshake keys for an SPDM secure session.
///
/// # Parameters
/// - `session`: Secure Session.
///
/// # Returns
/// - `Result<(), SpdmError>`: Returns `Ok(())` if the handshake keys are generated successfully, or an error code.
async fn generate_session_handshake_keys(&self, session: &mut SpdmSecureSession) -> Result<(), SpdmError>;
/// Generate data keys for an SPDM secure session.
///
/// # Parameters
/// - `session`: SPDM Secure Session.
///
/// # Returns
/// - `Result<(), SpdmError>`: Returns `Ok(())` if the data keys are generated successfully, or an error code.
async fn generate_session_data_keys(&self, session: &mut SpdmSecureSession) -> Result<(), SpdmError>;
/// Query if the last session is active.
///
/// # Returns
/// - `bool`: True if the last session is active, false otherwise.
fn is_last_session_id_valid(&self) -> bool;
/// Get the last session id.
///
/// # Returns
/// - `u32`: Last session id.
fn get_last_session_id(&self) -> u32;
/// Reset the last session id validity.
fn reset_last_session_id_validity(&self);
/// Decode a secure message. This includes MAC verification and optionally decryption.
///
/// # Parameters
/// - `request`: SPDM request message to be decoded.
///
/// # Returns
/// - `Result<(), SpdmError>`: Returns `Ok(())` if the secure message is decoded successfully, or an error code.
async fn decode_secure_message(&self, request: &mut [u8]) -> Result<(), SpdmError>;
/// Encode a secure message. This includes MAC generation and optionally encryption.
///
/// # Parameters
/// - `response`: SPDM response message to be encoded.
///
/// # Returns
/// - `Result<(), SpdmError>`: Returns `Ok(())` if the secure message is encoded successfully, or an error code.
async fn encode_secure_message(&self, response: &mut [u8]) -> Result<(), SpdmError>;
}
a6784b6
DOE Stack
The Caliptra subsystem supports SPDM, Secure-SPDM over PCI Data Object Exchange (DOE) mailbox protocol. The following diagram gives the over view of the DOE send and receive stack.
DOE Receive stack:
DOE Send stack:
sequenceDiagram
participant Host as "Host(TSM)"
participant SoC_PCI_DOE_FSM as "SoC PCI DOE Listener"
participant MCU_DOE_TRANSPORT_DRIVER as "MCU DOE Transport Driver"
participant DOE_CAPSULE as "DOE Capsule"
participant SPDM_APP as "SPDM App"
SPDM_APP -->> DOE_CAPSULE: App invokes receive_message<br> for SPDM or Secure-SPDM Data Object type
Host ->> Host: Host waits until<br> the `DOE Busy` bit is cleared in <br>DOE Status Register
loop While there is remaining DOE object to send
Host ->> SoC_PCI_DOE_FSM : Host starts sending DOE data object
SoC_PCI_DOE_FSM ->> SoC_PCI_DOE_FSM: Prepare the message in staging area <br> (eg: Mailbox or shared memory)
Note right of Host: Repeat until Host sets DOE Go bit
Host ->> SoC_PCI_DOE_FSM: Host writes `DOE Go` bit <br>in DOE Control Register<br> to indicate message ready
end
SoC_PCI_DOE_FSM ->> MCU_DOE_TRANSPORT_DRIVER: Notify that a new DOE object<br> is available to consume
MCU_DOE_TRANSPORT_DRIVER ->> DOE_CAPSULE: receive() callback
alt if DOE object is `Data Object 0`
DOE_CAPSULE ->> DOE_CAPSULE: Copy DOE object payload <br>into local buffer
DOE_CAPSULE ->> DOE_CAPSULE: Handle DOE Discovery
DOE_CAPSULE ->> DOE_CAPSULE: Prepare DOE Discovery response object
else if DOE object is `Data Object 1 or 2`
DOE_CAPSULE ->> DOE_CAPSULE: Copy DOE object payload <br>into app buffer
DOE_CAPSULE -->> SPDM_APP: Invoke upcall to userspace<br> to receive() message
end
DOE_CAPSULE ->> MCU_DOE_TRANSPORT_DRIVER: set_rx_buffer()<br> to set the receive buffer for the next DOE object
SPDM_APP ->> SPDM_APP: App processes message <br>and prepares DOE response
SPDM_APP -->> DOE_CAPSULE: App invokes send_message <br>to send DOE response
DOE_CAPSULE ->> MCU_DOE_TRANSPORT_DRIVER: invoke transmit()<br> to send the DOE response
MCU_DOE_TRANSPORT_DRIVER ->> SoC_PCI_DOE_FSM: Notify that DOE response is ready to send
SoC_PCI_DOE_FSM ->> Host: Set `Data Object Ready` bit in<br> DOE Status Register
DOE Capsule
The DOE capsule implements the system calls for the user space applications to send and receive the DOE data objects.
During board initialization, a DoeDriver instance is created and registered with a unique driver number. This instance manages the handling of DOE Discovery (Data Object Type 0), SPDM (Data Object Type 1), and Secure-SPDM (Data Object Type 2) data objects.
/// PCI-SIG Vendor ID that defined the data object type
const PCI_SIG_VENDOR_ID: u16 = 0x0001;
/// Data Object Protocol
const DATA_OBJECT_PROTOCOL_DOE_DISCOVERY: u8 = 0x00;
const DATA_OBJECT_PROTOCOL_CMA_SPDM: u8 = 0x01;
const DATA_OBJECT_PROTOCOL_SECURE_CMA_SPDM: u8 = 0x02;
pub const DOE_SPDM_DRIVER_NUM: usize = 0xA000_0010;
/// IDs for subscribe calls
mod upcall {
/// Callback for when the message is received
pub const MESSAGE_RECEIVED: usize = 0;
/// Callback for when the message is transmitted.
pub const MESSAGE_TRANSMITTED: usize = 1;
/// Number of upcalls
pub const COUNT: u8 = 2;
}
/// IDs for read-only allow buffers
mod ro_allow {
/// Buffer for the message to be transmitted
pub const MESSAGE_WRITE: usize = 0;
/// Number of read-only allow buffers
pub const COUNT: u8 = 1;
}
/// IDs for read-write allow buffers
mod rw_allow {
/// Buffer for the message to be received
pub const MESSAGE_READ: usize = 0;
/// Number of read-write allow buffers
pub const COUNT: u8 = 1;
}
#[derive(Default)]
pub struct App {
waiting_rx: Cell<bool>, // Indicates if a message is waiting to be received
pending_tx: Cell<bool>, // Indicates if a message is in progress
}
pub struct DoeDriver {
doe_transport: & dyn DoeTransport,
apps: Grant<
App,
UpcallCount<{ upcall::COUNT }>,
AllowRoCount<{ ro_allow::COUNT }>,
AllowRwCount<{ rw_allow::COUNT }>,
>,
current_app: Cell<Option<ProcessId>>,
}
DOE Transport Trait
The DOE Transport trait defines a platform-agnostic interface for sending and receiving DOE data objects. Integrators must provide a SoC-specific implementation of this trait to enable PCI-DOE communication with the host.
pub trait DoeTransportTxClient<'a> {
/// Called by driver to notify that the DOE data object transmission is done.
///
/// # Arguments
/// * `result` - Result indicating success or failure of the transmission
fn send_done(&self, result: Result<(), ErrorCode>);
}
pub trait DoeTransportRxClient {
/// Called to receive a DOE data object.
///
/// # Arguments
/// * `rx_buf` - buffer containing the received DOE data object
/// * `len_dw` - The length of the data received in dwords
fn receive(&self, rx_buf: &'static mut [u32], len_dw: usize);
}
pub trait DoeTransport<'a> {
/// Sets the transmit and receive clients for the DOE transport instance
fn set_tx_client(&self, client: &'a dyn DoeTransportTxClient<'a>);
fn set_rx_client(&self, client: &'a dyn DoeTransportRxClient);
/// Sets the buffer used for receiving incoming DOE Objects.
/// This should be called in receive()
fn set_rx_buffer(&self, rx_buf: &'static mut [u32]);
/// Gets the maximum size of the data object that can be sent or received over DOE Transport.
fn max_data_object_size_dw(&self) -> usize;
/// Enable the DOE transport driver instance.
fn enable(&self);
/// Disable the DOE transport driver instance.
fn disable(&self);
/// Send DOE Object to be transmitted over SoC specific DOE transport.
///
/// # Arguments
/// * `tx_buf` - Iterator that yields u32 values from data object to be transmitted.
/// * `len` - The length of the message in dwords (4-byte words).
fn transmit(&self, tx_buf: impl Iterator<Item = u32>, len_dw: usize) -> Result<(), ErrorCode>;
}
a6784b6
IDE_KM - Integrity and Data Encryption Key Management Protocol Support
The Caliptra subsystem enables IDE_KM protocol support over SPDM secure sessions by transmitting IDE_KM messages as application data. The IDE_KM protocol manages the provisioning of encryption keys for IDE streams, providing confidentiality, integrity, and replay protection for Translation Layer Packets (TLPs).
To implement IDE_KM, devices must provide the IdeDriver trait implementation. This trait defines the interfaces and configuration necessary for secure IDE key management. This documentation describes how to integrate IDE_KM with the Caliptra subsystem, outlines implementation requirements, and offers guidance for usage.
#![allow(unused)] fn main() { pub const IDE_STREAM_KEY_SIZE_DW: usize = 8; pub const IDE_STREAM_IV_SIZE_DW: usize = 2; pub const MAX_SELECTIVE_IDE_ADDR_ASSOC_BLOCK_COUNT: usize = 15; /// Port Configuration structure contains the configuration and capabilities for a specific IDE port. pub struct PortConfig { port_index: u8, function_num: u8, bus_num: u8, segment: u8, max_port_index: u8, } /// IDE Capability and Control Register Block pub struct IdeRegBlock { ide_cap_reg: IdeCapabilityReg, ide_ctrl_reg: IdeControlReg, } /// Link IDE Register Block pub struct LinkIdeStreamRegBlock { ctrl_reg: LinkIdeStreamControlReg, status_reg: LinkIdeStreamStatusReg, } /// Selective IDE Stream Register Block pub struct SelectiveIdeStreamRegBlock { capability_reg: SelectiveIdeStreamCapabilityReg, ctrl_reg: SelectiveIdeStreamControlReg, status_reg: SelectiveIdeStreamStatusReg, rid_association_reg_1: SelectiveIdeRidAssociationReg1, rid_association_reg_2: SelectiveIdeRidAssociationReg2, addr_association_reg_block: [AddrAssociationRegBlock; MAX_SELECTIVE_IDE_ADDR_ASSOC_BLOCK_COUNT], } /// IDE Address Association Register Block pub struct AddrAssociationRegBlock { reg1: IdeAddrAssociationReg1, reg2: IdeAddrAssociationReg2, reg3: IdeAddrAssociationReg3, } /// IDE Driver Error Types pub enum IdeDriverError { InvalidPortIndex, UnsupportedPortIndex, InvalidStreamId, InvalidArgument, GetPortConfigFail, KeyProgFail, KeySetGoFail, KeySetStopFail, NoMemory, } pub type IdeDriverResult<T> = Result<T, IdeDriverError>; /// KeyInfo structure contains information about the key set, direction, and sub-stream. bitfield! { #[derive(Clone, Copy, PartialEq, Eq)] pub struct KeyInfo(u8); impl Debug; pub key_set_bit, set_key_set_bit: 0; pub key_direction, set_key_direction: 1; reserved, _: 3, 2; pub key_sub_stream, set_key_sub_stream: 7, 4; } /// IDE Driver Trait /// /// Provides an interface for Integrity and Data Encryption (IDE) key management operations. /// This trait abstracts hardware-specific implementations for different platforms. #[async_trait] pub trait IdeDriver { /// Get the port configuration for a given port index. /// /// # Arguments /// * `port_index` - The index of the port to retrieve the configuration for. /// /// # Returns /// A result containing the `PortConfig` for the specified port index, or an error /// if the port index is invalid or unsupported. fn port_config(&self, port_index: u8) -> IdeDriverResult<PortConfig>; /// Get the IDE register block. /// /// # Arguments /// * `port_index` - The index of the port. /// /// # Returns /// A result containing the `IdeRegBlock` for the specified port, or an error fn ide_register_block(&self, port_index: u8) -> IdeDriverResult<IdeRegBlock>; /// Get the link IDE register block for a specific port and block index. /// /// # Arguments /// * `port_index` - The index of the port. /// * `block_index` - The index of the register block. /// /// # Returns /// A result containing the `LinkIdeStreamRegBlock` for the specified port and block fn link_ide_reg_block( &self, port_index: u8, block_index: u8, ) -> IdeDriverResult<LinkIdeStreamRegBlock>; /// Get the selective IDE register block for a specific port and block index. /// /// # Arguments /// * `port_index` - The index of the port. /// * `block_index` - The index of the register block. /// /// # Returns /// A result containing the `SelectiveIdeStreamRegBlock` for the specified port and block fn selective_ide_reg_block( &self, port_index: u8, block_index: u8, ) -> IdeDriverResult<SelectiveIdeStreamRegBlock>; /// Key programming for a specific port and stream. /// /// # Arguments /// * `stream_id` - Stream ID /// * `key_info` - Key information containing key set bit, direction, and sub-stream. /// * `port_index` - Port to which the key is to be programmed. /// * `key` - The key data to be programmed (8 DWORDs). /// * `iv` - The initialization vector (2 DWORDs). /// /// # Returns /// A result containing the status of the key programming operation: /// - `00h`: Successful /// - `01h`: Incorrect Length /// - `02h`: Unsupported Port Index value /// - `03h`: Unsupported value in other fields /// - `04h`: Unspecified Failure async fn key_prog( &self, stream_id: u8, key_info: KeyInfo, port_index: u8, key: &[u32; IDE_STREAM_KEY_SIZE_DW], iv: &[u32; IDE_STREAM_IV_SIZE_DW], ) -> IdeDriverResult<u8>; /// Start using the key set for a specific port and stream. /// /// # Arguments /// * `stream_id` - Stream ID /// * `key_info` - Key information containing key set bit, direction, and sub-stream. /// * `port_index` - Port to which the key set is to be started. /// /// # Returns /// A result containing the updated `KeyInfo` after starting the key set, or an /// error if the operation fails. async fn key_set_go( &self, stream_id: u8, key_info: KeyInfo, port_index: u8, ) -> IdeDriverResult<KeyInfo>; /// Stop the key set for a specific port and stream. /// /// # Arguments /// * `stream_id` - Stream ID /// * `key_info` - Key information containing key set bit, direction, and sub-stream. /// * `port_index` - Port to which the key set is to be stopped /// /// # Returns /// A result containing the updated `KeyInfo` after stopping the key set, or an error /// if the operation fails. async fn key_set_stop( &self, stream_id: u8, key_info: KeyInfo, port_index: u8, ) -> IdeDriverResult<KeyInfo>; } }
a6784b6
TDISP (TEE Device Interface Security Protocol) Support
Caliptra Subsystem supports handling of TDISP messages by processing them as VENDOR_DEFINED_REQUEST/VENDOR_DEFINED_RESPONSE message payloads. These messages are transported and processed within the secure session established between the host and the TDISP device as specified by Secured CMA/SPDM.
To facilitate the TDISP protocol, the devices must implement TdispDriver trait as defined below.
#![allow(unused)] fn main() { /// Error codes returned by TDISP driver pub enum TdispDriverError { /// Input parameter is null or invalid. InvalidArgument, /// Memory allocation failed. NoMemory, /// The driver failed to get TDISP capabilities. GetTdispCapabilitiesFail, /// The driver failed to get the device interface state. GetDeviceInterfaceStateFail, /// The driver failed to lock the device interface. LockInterfaceReqFail, /// The driver failed to start the device interface. StartInterfaceReqFail, /// The driver failed to stop the device interface. StopInterfaceReqFail, /// The driver failed to get the device interface report. GetInterfaceReportFail, /// The driver failed to get the mmio ranges. GetMmioRangesFail, /// The driver function is not implemented. FunctionNotImplemented, } pub type TdispDriverResult<T> = Result<T, TdispDriverError>; /// FunctionID of the device hosting the TDI. bitfield! { #[derive(FromBytes, IntoBytes, Immutable, Default)] #[repr(C)] pub struct FunctionId(u32); impl Debug; u16; pub requester_id, set_requester_id: 15, 0; // Bits 15:0 Requester ID u8; pub requester_segment, set_requester_segment: 23, 16; // Bits 23:16 Requester Segment pub requester_segment_valid, set_requester_segment_valid: 24, 24; // Bit 24 Requester Segment Valid reserved, _: 31, 25; // Bits 31:25 Reserved } /// TDISP Responder Capabilities pub struct TdispResponderCapabilities { dsm_capabilities: u32, req_msgs_supported: [u8; 16], lock_interface_flags_supported: u16, reserved: [u8; 3], dev_addr_width: u8, num_req_this: u8, num_req_all: u8, } /// Parameters passed along with the LOCK_INTERFACE_REQUEST pub struct TdispLockInterfaceParam { flags: TdispLockInterfaceFlags, default_stream_id: u8, reserved: u8, mmio_reporting_offset: [u8; 8], bind_p2p_addr_mask: [u8; 8], } /// TDISP Interface flags bitfield! { #[repr(C)] pub struct TdispLockInterfaceFlags(u16); impl Debug; u8; pub no_fw_update, set_no_fw_update: 0, 0; // Bit 0 NO_FW_UPDATE pub system_cache_line_size, set_system_cache_line_size: 1, 1; // Bits 1:1 SYSTEM_CACHE_LINE_SIZE pub lock_msix, set_lock_msix: 2, 2; // Bit 2 LOCK_MSIX pub bind_p2p, set_bind_p2p: 3, 3; // Bit 3 BIND_P2P pub all_req_redirect, set_all_req_redirect: 4, 4; // Bit 4 ALL_REQUEST_REDIRECT pub reserved, _: 15, 5; // Bits 15:5 Reserved } /// TDI Status pub enum TdiStatus { ConfigUnlocked = 0, ConfigLocked = 1, Run = 2, Error = 3, Reserved, } /// TDISP Driver trait that defines the interface for TDISP operations. /// This trait is intended to be implemented by a TDISP driver /// that interacts with the TDISP device. #[async_trait] pub trait TdispDriver: Send + Sync { /// Gets the TDISP device capabilities. /// /// # Arguments /// * `req_caps` - Requester (TSM) capability flags /// * `resp_caps` - Responder (DSM) capability flags /// /// # Returns /// 0 on success or an error response code as per the TDISP specification on failure. async fn get_capabilities( &self, req_caps: TdispReqCapabilities, resp_caps: &mut TdispRespCapabilities, ) -> TdispDriverResult<u32>; /// Lock Interface Request /// /// # Arguments /// * `function_id` - Device Interface Function ID /// * `param` - Lock Interface parameters from the request /// /// # Returns /// 0 on success or an error response code as per the TDISP specification on failure. async fn lock_interface( &mut self, function_id: FunctionId, param: TdispLockInterfaceParam, ) -> TdispDriverResult<u32>; /// Get the length of the device interface report. /// /// # Arguments /// * `function_id` - Device Interface Function ID /// * `intf_report_len` - Total device interface report length(output) /// /// # Returns /// Length of the device interface report on success or an error response code. async fn get_device_interface_report_len( &self, function_id: FunctionId, intf_report_len: &mut u16, ) -> TdispDriverResult<u32>; /// Get the device interface report. /// /// # Arguments /// * `function_id` - Device Interface Function ID /// * `offset` - Offset from the start of the report requested /// * `report` - report buffer slice to fill /// * `copied` - Length of the TDI report copied /// /// /// # Returns /// 0 on success or an error response code as per the TDISP specification on failure. async fn get_device_interface_report( &self, function_id: FunctionId, offset: u16, report: &mut [u8], copied: &mut usize, ) -> TdispDriverResult<u32>; /// Get the device interface state. /// /// # Arguments /// * `function_id` - Device Interface Function ID /// * `tdi_state` - Device Interface State to fill /// /// # Returns /// 0 on success or an error response code as per the TDISP specification on failure. async fn get_device_interface_state( &self, function_id: FunctionId, tdi_state: &mut TdiStatus, ) -> TdispDriverResult<u32>; /// Start the device interface. /// /// # Arguments /// * `function_id` - Device Interface Function ID /// /// # Returns /// 0 on success or an error response code as per the TDISP specification on failure. async fn start_interface(&mut self, function_id: FunctionId) -> TdispDriverResult<u32>; /// Stop the device interface. /// /// # Arguments /// * `function_id` - Device Interface Function ID /// /// # Returns /// 0 on success or an error response code as per the TDISP specification on failure. async fn stop_interface(&mut self, function_id: FunctionId) -> TdispDriverResult<u32>; } }
a6784b6
In-field Provisioning and Management of SPDM Certificate Slots
This document provides guidance for provisioning and managing certificate slots on Caliptra devices via the SPDM protocol, conforming to the OCP Device Identity Provisioning Specification.
Supporting the multiple PKI ownership model defined by OCP requires SPDM Responder to support multiple asymmetric key pairs in the connection (MULTI_KEY_CONN_RSP is true). Consequently, OCP Device Identity Provisioning features requires SPDM version 1.3 or later.
Caliptra Device Identity Key Pairs Discovery
Caliptra device shall support the following 3 key pairs for certificate slot provisioning and management:
LDevIDKey Pair (SPDM Key pair ID 1)FMC AliasKey Pair (SPDM Key pair ID 2)RT AliasKey Pair (SPDM Key pair ID 3)
The IdevID key pair is preβprovisioned by the vendor in SPDM certificate slot 0 and is treated as nonβconfigurable by the implementation.
The information about these key pairs can be retrieved by issuing the GET_KEY_PAIR_INFO request. The SPDM responder sets the GET_KEY_PAIR_INFO_CAP capability bit to advertise support for KEY_PAIR_INFO response messages. The implementation does not allow the requester to modify any parameters associated with Caliptra key pairs; as a result, the SET_KEY_PAIR_INFO_CAP capability bit remains cleared.
SPDM Certificate Slot mapping to OCP PKI entities
The default SPDM certificate slot mapping to OCP PKI entities is as follows:
- Slot 0: Vendor (pre-provisioned, fixed and read-only)
- Slot 1: Unused (available for future use)
- Slot 2: Owner (provisionable by Owner PKI)
- Slot 3: Tenant (provisionable by Tenant PKI)
But the actual mapping can be discovered by the SPDM Requester using the OCP_GET_SLOT_ID_MAPPING request.
Provisioning workflows using SPDM protocol
It is assumed that the Vendor slot is preβprovisioned and meets the SPDM requirements for initial device attestation. The Vendor slot may also be used to establish a secure session with the SPDM Requester (Owner/Tenant PKI) for subsequent certificateβslot provisioning.
The following sequence diagram illustrates the workflow for provisioning Owner certificate slots using the SPDM protocol.
sequenceDiagram
participant SPDMRequester as SPDM Requester/PKI Owner
participant MCU as Caliptra MCU/SPDM Responder
participant CaliptraRT
Note over SPDMRequester,MCU: ...<br/> 1. Perform initial Device Attestation using Vendor Slot. <br/>The requester has the device identity certificates.<br/> ...
opt
Note over SPDMRequester,MCU: 2. Retrieve Device Identity Key Pair Info
SPDMRequester->>+MCU: GET_KEY_PAIR_INFO (KeyPairID:1 (LDevID))
MCU->>-SPDMRequester: KEY_PAIR_INFO (TotalKeyPairs: 3, KeyPairID: 1, AssocCertSlotMask: 0x00)
end
opt
Note over SPDMRequester,MCU: 3. Discover SPDM Slot ID mapping for different OCP PKI entities
SPDMRequester->>+MCU: OCP_GET_SLOT_ID_MAPPING
MCU->>-SPDMRequester: OCP_SLOT_ID_MAPPING (Vendor:0, Owner: 2, Tenant:3)
end
Note over SPDMRequester,MCU: 4. Generate Envelope Signed CSR <br/> for key pair ID 1
SPDMRequester->>+MCU: GET_ENVELOPE_SIGNED_CSR (KeyPairID: 1, Nonce: nonce)
MCU->>+CaliptraRT: Request envelope signed LDevID CSR (nonce)
CaliptraRT->>-MCU: Envelope signed LDevID CSR signed by RT alias key
MCU->>-SPDMRequester: ENVELOPE_SIGNED_CSR (Envelope signed CSR data)
SPDMRequester-->>SPDMRequester: Validate the CSR and <br/>Issue endorsement certificate for LDevID
critical Within secure session and/or with requester authorization
Note over SPDMRequester,MCU: 5. Complete Owner Slot Provisioning
SPDMRequester->>+MCU: SET_CERTIFICATE (SlotID: 2, KeyPairID: 1, CertChain)
MCU->>-SPDMRequester:SET_CERTIFICATE_RSP (SlotID: 2)
end
Note over SPDMRequester,MCU: 6. Verify Owner Slot Certificate Installation
SPDMRequester->>+MCU: GET_KEY_PAIR_INFO (KeyPairID: 1)
MCU->>-SPDMRequester: KEY_PAIR_INFO (TotalKeyPairs: 3, KeyPairID: 1, AssocCertSlotMask: 0x04)
opt Get installed certificate chain and validate
SPDMRequester->>+MCU: GET_CERTIFICATE (SlotID: 2)
MCU->>-SPDMRequester: CERTIFICATE (SlotID: 2, CertChain)
SPDMRequester-->>SPDMRequester: Validate the installed certificate chain
end
Note over SPDMRequester,MCU: ...<br/> 7. Perform attestation using the newly installed Owner slot
Envelope-signed CSR generation
The MCU's SPDM responder supports retrieval of an envelopeβsigned Certificate Signing Request (CSR) for a specified Device Identity Key-pair ID via the OCP vendorβdefined GET_ENVELOPE_SIGNED_CSR command. Upon receiving this request, the MCU forwards it to the Caliptra RT firmware, which generates the CSR and returns it encapsulated in an EAT (Entity Attestation Token). The EAT is signed using the RT Alias key pair. The nonce provided in the request is forwarded to the RT firmware and included in the EAT to ensure freshness.
Authorization and Security Considerations
TBD
a6784b6
PLDM Stack
Overview
The Platform Level Data Model (PLDM) is a suite of specifications that define a common data model and message formats for communication between management controllers and managed devices. It is designed to standardize the way devices communicate in a platform management environment, enabling interoperability and simplifying the management of hardware components. In the context of Caliptra MCU, PLDM Base Protocol and PLDM for Firmware Update Protocol are supported by the PLDM stack to facilitate the following use cases:
-
PLDM message control and discovery: This feature enables the device to be discovered by the platform management controller (typically a BMC) following MCTP enumeration. It forms the foundation for running other PLDM protocols.
-
Streaming boot remainder firmware: The PLDM firmware update protocol defines standardized messages and data structures for obtaining firmware code and data. The MCU leverages it to stream boot the remainder firmware, which is any vendor-specific SoC or other firmware. There are several customized amendments to the PLDM firmware update specification to enable streaming boot and automatic activation. Details are available in the OCP whitepaper, Flashless Boot using OCP, PCIe, and DMTF Standards.
-
Impactless firmware update: PLDM firmware update over MCTP is a well-established approach for firmware updates, supporting multiple firmware components within a single package. Updates can be applied to a subset of components supported by the Firmware Device (FD), which is a valuable property to enable impactless updates. Details can be found in the firmware update spec.
Architecture
The PLDM stack in MCU runtime is a modular implementation that supports the PLDM base protocol as a responder and the PLDM firmware update protocol as a Firmware Device (FD). It operates in the userspace of MCU runtime and interacts with the MCTP transport layer to handle communication.
PLDM Stack for Base Protocol
- PLDM service
- Listens for incoming PLDM requests.
- Extracts the command opcode from the request and invokes the corresponding handler to process.
- Sends the response via the MCTP transport layer.
- Command handler
- Interacts with the message decoding library to decode the request.
- Executes the command.
- Encodes the response by interacting with the message encoding library.
PLDM Stack for Firmware Update Protocol
-
PLDM service
- Listens for incoming firmware update requests and responses.
- Extracts the command opcode from the request or response and invokes the corresponding handler to process.
- Sends the response via the MCTP transport layer.
-
Firmware update service
-
Firmware update command handler
- Decodes the request or response.
- Executes the appropriate actions based on the command and interacts with the state machine to trigger the state transition.
- Encodes responses by interacting with the message encoding library.
-
Firmware update state control
- Maintains the current state of the firmware update process.
- Provides mechanisms to transition between different states, ensuring the correct sequence of operations.
- Interacts with the firmware update service to manage state transitions based on the stage of the update process and signals the request initiator to process the outgoing request.
-
Request initiator
- Listens for the signal from state control to process the outgoing request.
- Interacts with the message encoding library to encode the request.
-
PLDM Base Protocol Sequence
The table below shows the command codes of the base protocol that are supported by the PLDM stack as a responder.
| Command Name | Command Code | Direction | Requirement |
|---|---|---|---|
GetTID | 0x02 | UA -> FD | Mandatory |
GetPLDMVersion | 0x03 | UA -> FD | Mandatory |
GetPLDMTypes | 0x04 | UA -> FD | Mandatory |
GetPLDMCommands | 0x05 | UA -> FD | Mandatory |
SetTID | 0x01 | UA -> FD | Optional |
The diagram below shows the PLDM message control and discovery sequence.
sequenceDiagram
participant UA as Update Agent
participant FD as Firmware Device
UA->>FD: GetTID
FD-->>UA: TID Response
UA->>FD: GetPLDMTypes
FD-->>UA: PLDMTypes Response (type-0, type-5)
UA->>FD: GetPLDMVersion for type-0
FD-->>UA: PLDMVersion Response
UA->>FD: GetPLDMCommands for type-0
FD-->>UA: PLDMCommands Response
UA->>FD: GetPLDMVersion for type-5
FD-->>UA: PLDMVersion Response
UA->>FD: GetPLDMCommands for type-5
FD-->>UA: PLDMCommands Response
PLDM Firmware Update Protocol Sequence
The table below shows the inventory commands and firmware update commands supported by the PLDM stack as FD.
| Command Name | Command Code | Direction | Requirement |
|---|---|---|---|
QueryDeviceIdentifiers | 0x01 | UA -> FD | Mandatory |
GetFirmwareParameters | 0x02 | UA -> FD | Mandatory |
RequestUpdate | 0x10 | UA -> FD | Mandatory |
PassComponentTable | 0x13 | UA -> FD | Mandatory |
UpdateComponent | 0x14 | UA -> FD | Mandatory |
RequestFirmwareData | 0x15 | FD -> UA | Mandatory |
TransferComplete | 0x16 | FD -> UA | Mandatory |
VerifyComplete | 0x17 | FD -> UA | Mandatory |
ApplyComplete | 0x18 | FD -> UA | Mandatory |
GetMetaData | 0x19 | FD -> UA | Mandatory |
ActivateFirmware | 0x1A | UA -> FD | Mandatory |
GetStatus | 0x1B | UA -> FD | Mandatory |
CancelUpdateComponent | 0x1C | UA -> FD | Mandatory |
CancelUpdate | 0x1D | UA -> FD | Mandatory |
The diagram below shows a complete PLDM firmware update sequence:
sequenceDiagram
participant UA as Update Agent
participant FD as Firmware Device
UA->>FD: QueryDeviceIdentifiers
FD-->>UA: DeviceIdentifiers Response
UA->>FD: GetFirmwareParameters
FD-->>UA: FirmwareParameters Response
UA->>FD: RequestUpdate
FD-->>UA: Update Response
UA->>FD: PassComponentTable
FD-->>UA: ComponentTable Response
UA->>FD: UpdateComponent
FD-->>UA: UpdateComponent Response
FD->>UA: RequestFirmwareData
UA-->>FD: FirmwareData Response
FD->>UA: TransferComplete
UA-->>FD: TransferComplete Response
Note over FD: Verifying component
FD->>UA: VerifyComplete
UA-->>FD: VerifyComplete Response
FD->>UA: ApplyComplete
UA-->>FD: ApplyComplete Response
UA->>FD: ActivateFirmware
FD-->>UA: ActivateFirmware Response
UA->>FD: GetStatus
FD-->>UA: Status Response
Interface
The PLDM stack is designed as a library that supports the PLDM base protocol as a responder and the PLDM firmware update protocol as a Firmware Device (FD). The diagram below shows the interface and components inside the stack. PldmFwUpdateServiceMgr serves as the interface between the PLDM stack and upper-level APIs, such as Firmware Update and Streaming Boot.
classDiagram
class PldmFwUpdateServiceMgr {
<<interface>>
+start_service() Result<(), PldmServiceError>
+stop_service() Result<(), PldmServiceError>
}
class PldmFwUpdateService {
+transport: T
+cmd_interface_responder: MessageResponder
+cmd_interface_requester: MessageRequester
+notification: Notifications
+start_service() Result<(), PldmServiceError>
+stop_service() Result<(), PldmServiceError>
}
class MessageResponder {
<<interface>>
+process_request(payload: &mut [u8]) Result<usize, MessageHandlerError>
+process_response(payload: &mut [u8]) Result<usize, MessageHandlerError>
}
class MessageRequester {
<<interface>>
+generate_request(payload: &mut [u8]) Result<usize, MessageHandlerError>
}
class CommandHandler {
<<interface>>
+execute(payload: &mut [u8], context: &mut PldmContext) usize
}
PldmFwUpdateServiceMgr <|-- PldmFwUpdateService
PldmFwUpdateService o-- MessageResponder
PldmFwUpdateService o-- MessageRequester
MessageResponder ..> CommandHandler : invoke
/// Trait representing a PLDM Firmware Update Service Manager.
///
/// This trait defines the necessary methods to start and stop the PLDM firmware update service.
///
/// # Methods
///
/// * `start_service` - Asynchronously starts the PLDM firmware update service.
/// This method creates an async task to listen for and process incoming requests.
/// * `stop_service` - Asynchronously stops the PLDM firmware update service.
///
/// # Errors
///
/// Both methods return a `Result` which, on error, contains a `PldmServiceError`.
pub trait PldmFwUpdateServiceMgr {
async fn start_service(&self) -> Result<(), PldmServiceError>;
async fn stop_service(&self) -> Result<(), PldmServiceError>;
}
pub struct PldmServiceError(pub NonZeroU32);
/// Represents a PLDM Firmware Update Service.
///
/// This struct is responsible for handling the PLDM firmware update process
/// using the specified transport, message responder, and message requester.
///
/// # Type Parameters
///
/// * `T` - A type that implements the `Mctp` trait, representing the transport layer.
/// * `U` - A type that implements the `MessageResponder` trait, representing the command interface responder.
/// * `R` - A type that implements the `MessageRequester` trait, representing the command interface requester.
/// * `N` - A type that implements the `Notifications` trait, representing the notification interface.
///
/// # Fields
///
/// * `transport` - The transport layer used for communication.
/// * `cmd_interface_responder` - The command interface responder.
/// * `cmd_interface_requester` - The command interface requester.
/// * `notification` - The notification interface to upper API.
/// * `other fields` - Additional fields required for the service.
pub struct PldmFwUpdateService<T: Mctp, U: MessageResponder, R: MessageRequester, N: Notifications> {
transport: T,
cmd_interface_responder: U,
cmd_interface_requester: R,
notifications: N,
// other fields
}
/// Trait representing the notification interface for the PLDM firmware update service.
///
/// This trait defines the necessary methods that will be called by the `PldmFwUpdateService`
/// to notify the upper-level API of specific events during the firmware update process.
pub trait Notifications {
fn on_request_update_received(&self, payload: &[u8]) -> Result<(), ErrCode>;
fn on_image_verify(&self, payload: &[u8]) -> Result<(), ErrCode>;
fn on_image_apply(&self, payload: &[u8]) -> Result<(), ErrCode>;
fn on_update_component_received(&self, payload: &[u8]) -> Result<(), ErrCode>;
fn on_activate_firmware_received(&self, payload: &[u8]) -> Result<(), ErrCode>;
}
impl PldmFwUpdateServiceMgr for PldmFwUpdateService {
async fn start_service(&self) -> Result<(), PldmServiceError> {};
async fn stop_service(&self) -> Result<(), PldmServiceError> {};
}
/// Trait representing a message responder that can process requests and responses asynchronously.
///
/// # Required Methods
///
/// - `process_request(&self, payload: &mut [u8]) -> Result<usize, MessageHandlerError>`:
/// Processes an incoming request message.
/// - `payload`: A mutable reference to the payload data to be processed.
/// - Returns a `Result` containing the size of the processed data or a `MessageHandlerError`.
///
/// - `process_response(&self, payload: &mut [u8]) -> Result<usize, MessageHandlerError>`:
/// Processes an incoming response message.
/// - `payload`: A mutable reference to the payload data to be processed.
/// - Returns a `Result` containing the size of the processed data or a `MessageHandlerError`.
///
/// # Errors
///
/// Both methods return a `Result` which, on failure, contains a `MessageHandlerError`.
pub trait MessageResponder: Send + Sync {
async fn process_request(&self, payload: &mut [u8]) -> Result<usize, MessageHandlerError>;
async fn process_response(&self, payload: &mut [u8]) -> Result<usize, MessageHandlerError>;
}
/// Trait representing a message requester that can generate requests asynchronously.
pub trait MessageRequester: Send + Sync {
/// Asynchronously generates a request with the given payload.
///
/// # Arguments
///
/// * `payload` - A mutable reference to a byte slice that will be used to generate the request.
///
/// # Returns
///
/// A `Result` containing the size of the generated request on success, or a `MessageHandlerError` on failure.
async fn generate_request(&self, payload: &mut [u8]) -> Result<usize, MessageHandlerError>;
}
pub struct MessageHandlerError(pub NonZeroU32);
/// Trait representing a command handler that can execute commands asynchronously.
///
/// # Required Methods
///
/// - `execute(&self, payload: &mut [u8], context: &mut PldmContext) -> usize`:
/// Executes a command with the given payload and context.
/// - `payload`: A mutable reference to the payload data to be processed.
/// - `context`: A mutable reference to the PLDM context.
/// - Returns the size of the processed data.
pub trait CommandHandler: Send + Sync {
async fn execute(&self, payload: &mut [u8], context: &mut PldmContext) -> usize;
}
/// Represents a PLDM Command Interface Responder.
///
/// This struct is responsible for handling the PLDM command interface
/// by maintaining a collection of command handlers and the PLDM context.
///
/// # Fields
///
/// * `handlers` - A heapless hash table that maps command codes to their respective command handlers.
/// * `context` - The PLDM context used for processing commands.
/// * `other fields` - Additional fields required for the command interface responder.
pub struct CmdInterfaceResponder<'a, const N: usize> {
handlers: heapless::FnvIndexMap<u8, &'a dyn CommandHandler, N>,
context: PldmContext,
// Other fields
}
pub struct CmdInterfaceRequester<'a, const N: usize> {
handlers: heapless::FnvIndexMap<u8, &'a dyn CommandHandler, N>,
// Other fields
}
a6784b6
Firmware Update
Overview
The MCU SDK offers a comprehensive API designed to facilitate firmware updates for Caliptra FMC & RT, MCU RT, and other SoC images. These updates are performed using the PLDM - T5 protocol and are supported for both streaming boot systems and flash boot systems.
Architecture
The MCU PLDM stack handles PLDM firmware messages from an external Firmware Update Agent. The stack generates upstream notifications to the Firmware Update API to handle application-specific actions such as writing firmware chunks to a staging or SPI Flash storage location, verifying components, etc through the Image Loading API. The API notifies the application of the start and completion of the firmware update process.
graph TD;
A[Application / Initiator] <--> B[API];
subgraph B[API]
direction LR
B1[Firmware Update] <--> B2[Image Loading];
B2 <--> B3[DMA]
B2 <--> B4[Flash]
B2 <--> B5[Mailbox]
end
B <--> C[PLDM];
PLDM Firmware Download Sequence
The diagram below shows the steps and interactions between different software layers during the firmware update process.
sequenceDiagram
title Firmware Update Service Initialization
actor App as Initiator
participant API as Firmware Update API
participant Firmware as PLDM Stack - T5
participant PLDM as Update Agent
App->>API: Start Firmware Update Service
loop for all components
API->>API: Retrieve firmware metadata from Caliptra core
end
API->>Firmware: Start Firmware Update Service
Firmware->>Firmware: Start listen loop
activate Firmware
Query Device Information
sequenceDiagram
title Query Device Information
actor App as Initiator
participant API as Firmware Update API
participant Firmware as PLDM Stack - T5
participant PLDM as Update Agent
PLDM->>Firmware: QueryDeviceIdentifiers
Firmware-->>PLDM: DeviceIdentifiers
PLDM->>Firmware: GetFirmwareParameters
Firmware-->>PLDM: FirmwareParameters
Request Update and Pass Components
sequenceDiagram
title Request Update and Pass Components
actor App as Initiator
participant API as Firmware Update API
participant Firmware as PLDM Stack - T5
participant PLDM as Update Agent
PLDM->>Firmware: RequestUpdate
Firmware->>API: Update Available Notification
API->>App: Update Available Notification
API-->>Firmware: Ok
Firmware-->>PLDM: RequestUpdate Response
loop until all component info passed
PLDM->>Firmware: PassComponent(component)
Firmware-->>PLDM: PassComponent Response
end
Updating Components
sequenceDiagram
title Updating Components
actor App as Initiator
participant API as Firmware Update API
participant Firmware as PLDM Stack - T5
participant PLDM as Update Agent
loop for every component
PLDM->>Firmware: UpdateComponent(component)
Firmware->>API: UpdateComponent Notification
alt Caliptra FMC+RT or SoC Manifest
API->>API: Acquire mailbox lock
else MCU RT or SoC Image
API->>API: GET_STAGING_ADDRESS
end
API-->>Firmware: Ok
Firmware-->>PLDM: UpdateComponent Response
end
Requesting and Transferring Firmware Data
sequenceDiagram
title Requesting and Transferring Firmware Data
actor App as Initiator
participant API as Firmware Update API
participant Firmware as PLDM Stack - T5
participant PLDM as Update Agent
loop until component is downloaded
Firmware->>PLDM: RequestFirmwareData
PLDM-->>Firmware: FirmwareData
Firmware->>API: FirmwareData Notification
alt SoC Manifest
API->>API: Stream as SoC_MANIFEST<br/>Mailbox command
else Caliptra FMC+RT
API->>API: Stream as CALIPTRA_FW_LOAD<br/>Mailbox Command
else MCU RT or SoC Image
API->>API: Write to Staging Area
end
API-->>Firmware: Ok
end
API-->>API: Release Mailbox Lock (if acquired)
Firmware-->>PLDM: Transfer Complete
Verifying Components
sequenceDiagram
title Verifying Components
actor App as Initiator
participant API as Firmware Update API
participant Firmware as PLDM Stack - T5
participant PLDM as Update Agent
Firmware->>API: Verify Component Notification
alt SoC Manifest
API->>API: Process SET_AUTH_MANIFEST<br/> Mailbox Command Response
else MCU RT or SoC image
API->>API: Verify through AUTHORIZE_AND_STASH<br/>Mailbox Command
end
API-->>Firmware: Ok
Firmware-->>PLDM: VerifyComplete
Applying Components
sequenceDiagram
title Applying Components
actor App as Initiator
participant API as Firmware Update API
participant Firmware as PLDM Stack - T5
participant PLDM as Update Agent
Firmware->>API: Apply component Notification
alt Firmware Update for devices with Flash
alt SoC Manifest
API->>API: Write SoC Manifest to Flash Storage
else MCU RT or SoC Image
API->>API: Copy image from Staging to Flash Storage
end
end
API->>Firmware: Ok
Firmware->>PLDM: ApplyComplete
Activating Firmware
sequenceDiagram
title Activating Firmware
actor App as Initiator
participant API as Firmware Update API
participant Firmware as PLDM Stack - T5
participant PLDM as Update Agent
PLDM->>Firmware: ActivateFirmware
Firmware->>API: Activate Notification
alt MCU RT or SoC Image
API->>API: Send Activate Image Mailbox Command
end
API-->>Firmware: Ok
API->>App: UpdateComplete
Firmware-->>PLDM: Activate Response
Firmware Update Flow
Full Image Update for Flash Boot System
Option 1: Updating the full flash image as a single PLDM firmware component
PLDM update packages natively support selecting applicable components using the ApplicableComponents bitfield in the package header. For the case of Component N + 1, it is treated as a single component by the PLDM Update Agent. This component can encapsulate multiple embedded images each with its corresponding image information entry, checksum and flash header. The structure and layout of Component N + 1 align with the flash layout definition in flash_layout.md.
| PLDM FW Update Package |
|---|
| Package Header Information |
| Firmware Dev ID Descriptors |
| Downstream Dev ID Descriptors |
| Component Image Information |
| Package Header Checksum |
| Package Payload Checksum |
| Component 1 (Caliptra FMC + RT) |
| Component 2 (SoC Manifest) |
| Component 3 (MCU RT) |
| Component 4 (SoC Image 1) |
| ... |
| Component N (SoC Image N-3) |
| Component N + 1 (Full image for flash-boot system) |
| Component N + 1 structure |
|---|
| Flash header | Checksum | Image Info (Caliptra FMC + RT) | | Image Info (SoC Manifest) | | Image Info (MCU RT) | | Image Info (SoC Image 1) | | ... | | Image Info (SoC Image N - 3) | | Caliptra FMC + RT | | SoC Manifest | | MCU RT | | SoC Image 1 | | ... | | SoC Image N - 3 |
To support full image updates, a SoC-defined staging memory must be provided to store the incoming payload. The designated staging area for Component 1 must be accessible by the Caliptra ROM to fetch and authorize the image. This staging area could be located in MCU SRAM or MCI mailbox SRAM, as defined in Caliptra 2.1. If the SoC-defined staging memory does not meet this requirement, the image must be copied to the compliant region, which may slightly impact performance. For other components, if the staging memory (e.g., a staging partition on flash) is not directly accessible by the Caliptra core's DMA engine for reading and hashing the image, the MCU must perform the cryptographic operations to compute the hash. The computed hash is then sent via a mailbox command for authorization.
Detailed steps: Note: Actions below are performed by MCU RT Firmware
- An initiator, such as a custom user application, starts the firmware update service through the Firmware Update API. This action initializes the responder loop in the PLDM stack, enabling it to listen for incoming PLDM messages from the PLDM agent. The API queries firmware component metadata from the Caliptra core (e.g., component version numbers, classifications, etc.) using a mailbox command. This metadata is used to construct the Device Identifiers and Firmware Parameters, as specified in the DMTF DSP0267 1.3.0 standard. (TBD: Confirm if the mailbox command can provide metadata for the full image.)
- The PLDM stack notifies the API when a firmware image becomes available for update.
- The PLDM stack notifies the API which component is being downloaded using the UpdateComponent notification.
- The PLDM stack sends a FirmwareData notification to the API for each received firmware chunk, including the data, size, and chunk offset. The API's download handler writes the received firmware data to the staging memory.
- Once all firmware chunks are downloaded, the PLDM stack notifies the API to verify the component. The API processes the component to extract and identify individual embedded images, referred to as subcomponents. The verification process is performed sequentially for each subcomponent:
a. For the Caliptra FMC + RT subcomponent, the MCU sends it to the Caliptra core using the
CALIPTRA_FW_UPLOADmailbox command. When this command is executed, Caliptra core firmware is authorized and activated in one shot. The new core image is taken into effect after core reset. b. For the SoC Manifest subcomponent, the MCU sends it to the Caliptra core using theSET_AUTH_MANIFESTmailbox command. The mailbox response confirms the authenticity and correctness of the manifest. c. For MCU RT or SoC Image subcomponents, the MCU sends theAUTHORIZE_AND_STASHmailbox command, indicating that the image to be verified resides in the staging area. - After verification, the PLDM stack notifies the API to apply the image. The MCU writes the images from the temporary staging area to the inactive flash partition. Refer to A/B Partition Mechanism for more details.
- When the Update Agent issues the
ActivateFirmwarecommand, the API updates the partition table to mark the inactive partition as active. The API may provide a handler to initiate a warm reset, enabling the new image to execute from flash.
Option 2: Updating the full flash image as multiple PLDM firmware components
In this approach, the full flash image is divided into 1 to N distinct firmware components. The ApplicableComponents bitfield in the PLDM package header identifies the selected components, while the component image information provides metadata for each component, including the total number of components. The PLDM Update Agent requests update on each component sequentially, adhering to the order specified in the component image information. Each component is verified and applied by the device. PLDM Update Agent issues ActivateFirmware command to inform the device to prepare all successfully applied components to become active at the next activation.
| PLDM FW Update Package |
|---|
| Package Header Information |
| Firmware Dev ID Descriptors |
| Downstream Dev ID Descriptors |
| Component Image Information |
| Package Header Checksum |
| Package Payload Checksum |
| Component 1 (Caliptra FMC + RT) |
| Component 2 (SoC Manifest) |
| Component 3 (MCU RT) |
| Component 4 (SoC Image 1) |
| ... |
| Component N (SoC Image N-3) |
| Component N + 1 (Full image for flash-boot system) |
Detailed steps:
Note: Actions below are performed by MCU RT Firmware
- An initiator, such as a custom user application, starts the firmware update service through the Firmware Update API. This action initializes the responder loop in the PLDM stack, enabling it to listen for incoming PLDM messages from the PLDM agent. The API queries firmware component metadata from the Caliptra core (e.g., component version numbers, classifications, etc.) using a mailbox command. This metadata is used to construct the Device Identifiers and Firmware Parameters, as specified in the DMTF DSP0267 1.3.0 standard. (TBD: Confirm if the mailbox command can provide metadata for the full image.)
- The PLDM stack notifies the API when a firmware image becomes available for update.
- The PLDM stack notifies the API which component is being downloaded using the UpdateComponent notification. The 1st firmware component received to update should be Caliptra FMC + RT.
- The PLDM stack sends a FirmwareData notification to the API for each received firmware chunk, including the data, size, and chunk offset. The API's download handler writes the received firmware data to the staging memory.
- Once all firmware chunks are downloaded, the PLDM stack notifies the API to verify the component.
a. If the component is Caliptra FMC+RT, MCU sends it to Caliptra core using the
CALIPTRA_FW_UPLOADmailbox command. b. If the component is a SoC Manifest, the mailbox via theSET_AUTH_MANIFESTmailbox command. c. If the component is an MCU RT or SoC Image, it is written to a staging area defined in SoC manifest. - After verification, the PLDM stack notifies the API to apply the image. The MCU writes the images from the temporary staging area to the inactive flash partition. Refer to A/B Partition Mechanism for more details.
- Repeat steps 3 through 6 for Component 2, 3, 4 ... N.
- After all firmware components have been transferred and applied, Update Agent issues
ActivateFirmwarecommand to inform the device to prepare all successfully applied components to become active at the next activation. The API updates the partition table to mark the inactive partition as active. The API may provide a handler to initiate a warm reset, enabling the new image to execute from flash.
A/B Partition Mechanism
The A/B partition mechanism is a robust approach to ensure seamless and reliable firmware updates for flash boot systems. When partition A is active, it contains the currently running firmware, while partition B remains inactive and is used as the target for firmware updates. This ensures that the system can always revert to the previous active partition in case of an update failure.
Partition Layout
The location of A/B partitions can either reside on a single flash device or be distributed across separate flash devices. In a single flash device setup, both partitions share the same physical storage, simplifying design and reducing costs. However, this approach may introduce performance bottlenecks during simultaneous read/write operations and poses a single point of failure. On the other hand, using separate flash devices for A/B partitions enhances redundancy and reliability, allowing parallel operations that improve update performance. This configuration, while more expensive and complex, is ideal for systems requiring high reliability and scalability. The choice between these configurations depends on the specific requirements of the system, such as cost constraints, performance needs, and reliability expectations.
| Partition A (Active) | Partition B (Inactive) |
|---|---|
| Flash header | Flash header |
| Checksum | Checksum |
| Image Info (Caliptra FMC + RT) | Image Info (Caliptra FMC + RT) |
| Image Info (SoC Manifest) | Image Info (SoC Manifest) |
| Image Info (MCU RT) | Image Info (MCU RT) |
| Image Info (SoC Image 1) | Image Info (SoC Image 1) |
| ... | ... |
| Image Info (SoC Image N - 3) | Image Info (SoC Image N - 3) |
| Caliptra FMC + RT | Caliptra FMC + RT |
| SoC Manifest | SoC Manifest |
| MCU RT | MCU RT |
| SoC Image 1 | SoC Image 1 |
| ... | ... |
| SoC Image N - 3 | SoC Image N - 3 |
Partition Selection
- Partition Table
For the A/B partition mechanism, the bootloader (MCU ROM) determines which partition to load the firmware image from by using a partition selection mechanism. The implementation of partition selection is system-specific. A common approach involves using a partition table stored in a reserved area of flash. The table below shows an example partition table format:
| Field Name | Size | Description |
|---|---|---|
| Active Partition | 1 byte | Indicates the active partition (A or B). |
| Partition A Status | 1 byte | Refer to Partition Status for values |
| Partition B Status | 1 byte | Refer to Partition Status for values. |
| Rollback Flag | 1 byte | Indicates if rollback is required. |
| Reserved | 4 byte | Reserved |
| CheckSum | 4 byte |
- Partition Status
Bits 7:4: Boot Attempt Count
Bits 3:0:
| Value | Description |
|---|---|
| 0 | Invalid |
| 1 | Valid |
| 2 | Boot Failed |
| 3 | Boot Successful |
- Partition Table Usage
- During Normal Boot
- The MCU ROM reads the partition table to determine:
- The active partition to boot from.
- Whether the active partition is valid and bootable.
- If the active partition is valid, the bootloader loads the firmware image from it and boots the system.
- If the firmware in the active partition fails to boot (e.g., due to corruption or verification failure), the bootloader:
- Checks the Rollback Flag.
- Switches to the other partition if rollback is required.
- The MCU ROM reads the partition table to determine:
- During Firmware Update
- In the
ActivateFirmwarephase, the partition table or status flags are updated to mark the inactive partition as the new active partition. - Steps to Update:
- Set the
Active Partitionfield to the inactive partition (A or B). - Optionally mark the previously active partition as inactive or valid for rollback.
- Write the updated partition table or status flags back to the reserved area in non-volatile memory.
- Set the
- In the
- During Normal Boot
Partial firmware update
Below are the supported scenarios:
-
Caliptra Core Firmware Update
- Updates the Caliptra FMC + Caliptra RT component. (Component 1 in PLDM package)
-
MCU Runtime Firmware Update
- Updates the SoC Manifest and MCU RT firmware together.(Component 2 and Component 3 in PLDM package)
- The updated SoC Manifest includes the hash entry for the new MCU RT firmware.
-
SoC Firmware Update
- Updates the SoC Manifest along with associated SoC images. (Component 2, Component 4.. N in PLDM package)
- The updated SoC Manifest contains hash entries for the new SoC firmware components.
These scenarios are designed to maintain system integrity and ensure seamless updates for both streaming boot system and flash boot system.
Detailed steps:
Note: Actions below are performed by MCU RT Firmware.
- An initiator (such as a custom user application) starts the firmware service through the Firmware Update API. This will start the responder loop in the PLDM stack that will listen for PLDM messages coming from the PLDM agent. The API queries firmware component metadata from the Caliptra core (e.g., component version numbers, classification, etc.) using a mailbox command to construct the Device Identifiers and Firmware Parameters, as defined by the DMTF DSP0267 1.3.0 specification, needed by the PLDM stack.
- The PLDM stack notifies the API if a firmware image is available for update.
- The PLDM stack notifies the API which component is being downloaded using the UpdateComponent notification. If the image is an MCU RT or SoC Image, the staging address is retrieved from the SoC Manifest stored in the Caliptra Core using a mailbox command. For Caliptra FMC+RT and the SoC Manifest, if it is flash boot system, staging address should be provided. Otherwise,the mailbox lock is acquired since these images are streamed directly through the mailbox interface. The lock is released after all chunks of the image have been transferred.
- The PLDM stack sends a FirmwareData notification to the API for each received firmware chunk, including the data, size, and chunk offset.
- If the component is a SoC Manifest, it is streamed to the mailbox via the SET_AUTH_MANIFEST mailbox command. If it is flash boot system, it is also written to staging area.
- If the component is Caliptra FMC+RT,it is streamed to the Caliptra core using the CALIPTRA_FW_UPLOAD mailbox command. If it is flash boot system, it is also written to staging area.
- If the component is an MCU RT or SoC Image, it is written to a staging area determined in step 3.
- Once all firmware chunks are downloaded, the PLDM stack notifies the API to verify the component.
- If the component is a SoC Manifest, the MCU waits for the SET_AUTH_MANIFEST mailbox command response, which indicates the authenticity and correctness of the manifest.
- If the component is an MCU RT or SoC Image, the MCU sends the AUTHORIZE_AND_STASH command, indicating that the image to be verified is in the staging area. Note: The AUTHORIZE_AND_STASH command computes the SHA of the image via the SHA-Acc by streaming the image from the staging area to the SHA-Acc through DMA. The computed SHA is compared against the SHA in the SoC Manifest for the specific image.
- After verification, the PLDM stack notifies the API to apply the image. The MCU writes the images to SPI Flash storage from the temporary staging area (if flash is available on the device).
- When the Update Agent sends the
ActivateFirmwarecommand, the API sends anActivateImagemailbox command to the Caliptra core. The Caliptra core processes the activation according to the Caliptra specification.
Interfaces
#![allow(unused)] fn main() { pub trait FirmwareUpdateApi { /// Start the firmware update service. /// /// # Returns /// Returns a future that will remain unset until the service is stopped. /// Ok(()) - The service has been terminated successfully. /// Err(FirmwareUpdateError) - The service has been terminated with an error. async fn start_service(&self) -> Result<(), FirmwareUpdateError>; /// Stop the firmware update service. /// /// # Returns /// Ok() - The service has been terminated successfully. /// Err(ErrorCode) - The service can not be stopped. fn stop_service(&self) -> Result<(), ErrorCode>; /// Register a callback to be called when a firmware update event occurs. /// /// # Arguments /// callback - The callback to be called when a firmware update event occurs. fn register_callback(&self, callback: FirmwareUpdateCallback); } /// Define the callback function signature for firmware update events. /// Returns Ok(()) if the notification is handled successfully, otherwise an error code. pub type FirmwareUpdateCallback = fn(FirmwareUpdateNotification) -> Result<(),ErrorCode>; pub enum FirmwareUpdateNotification<'a>{ // Firmware Update is available and ready for download. UpdateAvailable, // Firmware Update is complete. UpdateComplete, // Firmware Update is canceled. UpdateCanceled, } }
a6784b6
External Mailbox (MCI Mailbox) Commands Spec
Overview
This document outlines the external mailbox commands that enable SoC agents to interact with the MCU via MCI mailbox. These commands support a wide range of functionalities, including querying device-specific information, retrieving debug and attestation logs, managing certificates, utilizing cryptographic services and secure debugging in production environment.
-
Device Identification and Capabilities
- Retrieve firmware versions, unique device identifiers, and device capabilities to ensure compatibility and proper configuration.
- Query device-specific information such as chip identifiers or subsystem details.
-
Debugging and Diagnostics
- Retrieve debug logs to analyze device behavior, diagnose issues, and monitor runtime states.
- Clear logs to reset diagnostic data and maintain storage efficiency.
-
Certificate Management
- Export Certificate Signing Requests (CSRs) for device keys to facilitate secure provisioning.
- Import signed certificates to establish a trusted certificate chain for device authentication.
-
Cryptographic Services
- AES encryption and decryption
- SHA hashing
- Random number generation
- Digital signing
- Signature verification
- Key exchange
-
Debug Unlock Mechanisms
- Facilitate secure debugging in production environments
- Ensure controlled access to debugging features
-
In-Field Fuse Provisioning
- See fuses spec for details.
Mailbox Commands List
| Name | Command Code | Description |
|---|---|---|
| MC_FIRMWARE_VERSION | 0x4D46_5756 ("MFWV") | Retrieves the version of the target firmware. |
| MC_DEVICE_CAPABILITIES | 0x4D43_4150 ("MCAP") | Retrieve the device capabilities. |
| MC_DEVICE_ID | 0x4D44_4944 ("MDID") | Retrieves the device ID. |
| MC_DEVICE_INFO | 0x4D44_494E ("MDIN") | Retrieves information about the target device. |
| MC_EXPORT_IDEV_CSR | 0x4D49_4352 ("MICR") | Exports the IDEVID Self-Signed Certificate Signing Request. |
| MC_IMPORT_IDEV_CERT | 0x4D49_4943 ("MIIC") | Allows SoC to import DER-encoded IDevId certificate on every boot. |
| MC_GET_LOG | 0x4D47_4C47 ("MGLG") | Retrieves the internal log for the RoT. |
| MC_CLEAR_LOG | 0x4D43_4C47 ("MCLG") | Clears the log in the RoT subsystem. |
| MC_FIPS_SELF_TEST_START | 0x4D46_5354 ("MFST") | Starts the FIPS self-test to exercise the crypto engine. |
| MC_FIPS_SELF_TEST_GET_RESULTS | 0x4D46_4752 ("MFGR") | Retrieves the results of the FIPS self-test. |
| MC_FIPS_PERIODIC_ENABLE | 0x4D46_5045 ("MFPE") | Enables or disables periodic FIPS self-test. |
| MC_FIPS_PERIODIC_STATUS | 0x4D46_5053 ("MFPS") | Retrieves the status of periodic FIPS self-test. |
| MC_SHA_INIT | 0x4D43_5349 ("MCSI") | Starts the computation of a SHA hash of data. |
| MC_SHA_UPDATE | 0x4D43_5355 ("MCSU") | Continues a SHA computation started by MC_SHA_INIT or another MC_SHA_UPDATE. |
| MC_SHA_FINAL | 0x4D43_5346 ("MCSF") | Finalizes the computation of a SHA and produces the hash of all the data. |
| MC_HMAC | 0x4D43_484D ("MCHM") | Computes an HMAC according to RFC 2104. |
| MC_HMAC_KDF_COUNTER | 0x4D43_4B43 ("MCKC") | Computes HMAC KDF in Counter Mode as specified in NIST SP800-108. |
| MC_HKDF_EXTRACT | 0x4D43_4B54 ("MCKT") | Implements HKDF-Extract as specified in RFC 5869. |
| MC_HKDF_EXPAND | 0x4D43_4B50 ("MCKP") | Implements HKDF-Expand as specified in RFC 5869. |
| MC_AES_ENCRYPT_INIT | 0x4D43_4349 ("MCCI") | Starts an AES encryption operation. |
| MC_AES_ENCRYPT_UPDATE | 0x4D43_4355 ("MCCU") | Continues an AES encryption operation started by MC_AES_ENCRYPT_INIT. |
| MC_AES_DECRYPT_INIT | 0x4D43_414A ("MCAJ") | Starts an AES-256 decryption operation. |
| MC_AES_DECRYPT_UPDATE | 0x4D43_4155 ("MCAU") | Continues an AES decryption operation started by MC_AES_DECRYPT_INIT. |
| MC_AES_GCM_ENCRYPT_INIT | 0x4D43_4749 ("MCGI") | Starts an AES-256-GCM encryption operation. |
| MC_AES_GCM_ENCRYPT_UPDATE | 0x4D43_4755 ("MCGU") | Continues an AES-GCM encryption operation started by MC_AES_GCM_ENCRYPT_INIT. |
| MC_AES_GCM_ENCRYPT_FINAL | 0x4D43_4746 ("MCGF") | Finalizes the AES-GCM encryption operation and produces the final ciphertext and tag. |
| MC_AES_GCM_DECRYPT_INIT | 0x4D43_4449 ("MCDI") | Starts an AES-256-GCM decryption operation. |
| MC_AES_GCM_DECRYPT_UPDATE | 0x4D43_4455 ("MCDU") | Continues an AES-GCM decryption operation started by MC_AES_GCM_DECRYPT_INIT. |
| MC_AES_GCM_DECRYPT_FINAL | 0x4D43_4446 ("MCDF") | Finalizes the AES-GCM decryption operation and verifies the tag. |
| MC_ECDH_GENERATE | 0x4D43_4547 ("MCEG") | Computes the first half of an Elliptic Curve Diffie-Hellman exchange. |
| MC_ECDH_FINISH | 0x4D43_4546 ("MCEF") | Computes the second half of an Elliptic Curve Diffie-Hellman exchange. |
| MC_ECDSA_CMK_PUBLIC_KEY | 0x4D43_4550 ("MCEP") | Generates an ECDSA public key from a CMK. |
| MC_ECDSA_CMK_SIGN | 0x4D43_4553 ("MCES") | Creates an ECDSA signature using a CMK. |
| MC_ECDSA_CMK_VERIFY | 0x4D43_4556 ("MCEV") | Validates an ECDSA signature using a CMK. |
| MC_RANDOM_STIR | 0x4D43_5253 ("MCRS") | Adds additional entropy to the internal deterministic random bit generator. |
| MC_RANDOM_GENERATE | 0x4D43_5247 ("MCRG") | Generates random bytes from the internal RNG. |
| MC_IMPORT | 0x4D43_494D ("MCIM") | Imports a specified key and returns a CMK for it. |
| MC_DELETE | 0x4D43_444C ("MCDL") | Deletes the object stored with the given mailbox ID. |
| MC_ECDSA384_SIG_VERIFY | 0x4D45_4356 ("MECV") | Verifies an ECDSA P-384 signature. |
| MC_LMS_SIG_VERIFY | 0x4D4C_4D56 ("MLMV") | Verifies an LMS signature. |
| MC_ECDSA384_SIGN | 0x4D45_4353 ("MECS") | Requests to sign a SHA-384 digest with the DPE leaf certificate. |
| MC_MLDSA_SIGN | 0x4D4C_4D53 ("MLMS") | Requests to sign a SHA-384 digest with the DPE leaf certificate using MLDSA. |
| MC_PRODUCTION_DEBUG_UNLOCK_REQ | 0x4D44_5552 ("MDUR") | Requests debug unlock in a production environment. |
| MC_PRODUCTION_DEBUG_UNLOCK_TOKEN | 0x4D44_5554 ("MDUT") | Sends the debug unlock token. |
| MC_FUSE_READ | 0x4946_5052 ("IFPR") | See fuses spec for details |
| MC_FUSE_WRITE | 0x4946_5057 ("IFPW") | See fuses spec for details |
| MC_FUSE_LOCK_PARTITION | 0x4946_504B ("IFPK") | See fuses spec for details |
Command Format
MC_FIRMWARE_VERSION
Retrieves the version of the target firmware.
Command Code: 0x4D46_5756 ("MFWV")
Table: MC_FIRMWARE_VERSION input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| index | u32 | - 00h = Caliptra core firmware |
- 01h = MCU runtime firmware | ||
- 02h = SoC firmware | ||
| Additional indexes are firmware-specific |
Table: MC_FIRMWARE_VERSION output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| version | u8[32] | Firmware Version Number in ASCII format |
MC_DEVICE_CAPABILITIES
Retrieve the device capabilites.
Command Code: 0x4D43_4150 ("MCAP")
Table: MC_DEVICE_CAPABILITIES input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 |
Table: MC_DEVICE_CAPABILITIES output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| caps | u8[32] | - Bytes [0:7]: Reserved for Caliptra RT |
| - Bytes [8:11]: Reserved for Caliptra FMC | ||
| - Bytes [12:15]: Reserved for Caliptra ROM | ||
| - Bytes [16:23]: Reserved for MCU RT | ||
| - Bytes [24:27]: Reserved for MCU ROM | ||
| - Bytes [28:31]: Reserved |
MC_DEVICE_ID
Retrieves the device ID.
Command Code: 0x4D44_4944 ("MDID")
Table: MC_DEVICE_ID input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 |
Table: MC_DEVICE_ID output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| vendor_id | u16 | Vendor ID; LSB |
| device_id | u16 | Device ID; LSB |
| subsystem_vendor_id | u16 | Subsystem Vendor ID; LSB |
| subsystem_id | u16 | Subsystem ID; LSB |
MC_DEVICE_INFO
Retrieves information about the target device.
Command Code: 0x4D44_494E ("MDIN")
Table: MC_DEVICE_INFO input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| index | u32 | Information Index: |
- 00h = Unique Chip Identifier | ||
| Additional indexes are firmware-specific |
Table: MC_DEVICE_INFO output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| data_size | u32 | Size of the requested data in bytes |
| data | u8[data_size] | Requested information in binary format |
MC_EXPORT_IDEV_CSR
Exports the IDEVID Self-Signed Certificate Signing Request.
Command Code: 0x4D49_4352 ("MICR")
Table: MC_EXPORT_IDEV_CSR input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| index | u32 | Information Index: |
- 00h = IDEVID ECC CSR | ||
- 01h = IDEVID MLDSA CSR |
Table: MC_EXPORT_IDEV_CSR output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| data_size | u32 | Length in bytes of the valid data in the data field. |
| data | u8[data_size] | DER-encoded IDevID certificate signing request. |
MC_IMPORT_IDEV_CERT
Allows SoC to import DER-encoded IDevId certificate on every boot. The IDevId certificate is added to the start of the certificate chain.
Command Code: 0x4D49_4943 ("MIIC")
Table: MC_IMPORT_IDEV_CERT input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| cert_size | u32 | Size of the DER-encoded IDevID certificate. |
| cert | u8[1024] | DER-encoded IDevID certificate. |
Table: MC_IMPORT_IDEV_CERT output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error. |
MC_GET_LOG
Retrieves the internal log for the RoT. There are two types of logs available: the Debug Log, which contains RoT application information and machine state, and the Attestation Measurement Log, which is similar to the TCG log.
Command Code: 0x4D47_4C47 ("MGLG")
Table: MC_GET_LOG input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over input data |
| log type | u32 | Type of log to retrieve: |
- 0 = Debug Log | ||
- 1 = Attestation Log |
Table: MC_GET_LOG output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error. |
| data_size | u32 | Size of the log data in bytes |
| data | u8[data_size] | Log contents |
Debug Log Format:
The debug log reported by the device has no specified format, as this can vary between different devices and is not necessary for attestation. It is expected that diagnostic utilities for the device will be able to understand the exposed log information. A recommended entry format is provided here:
| Offset | Description |
|---|---|
| 1:7 | Log Entry Header |
| 8:9 | Format of the entry (e.g., 1 for current format) |
| 10 | Severity of the entry |
| 11 | Identifier for the component that generated the message |
| 12 | Identifier for the entry message |
| 13:16 | Message-specific argument |
| 17:20 | Message-specific argument |
MC_CLEAR_LOG
Clears the log in the RoT subsystem.
Command Code: 0x4D43_4C47 ("MCLG")
Table: MC_CLEAR_LOG input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over input data |
| log type | u32 | Type of log to retrieve: |
- 0 = Debug Log | ||
- 1 = Attestation Log |
Table: MC_CLEAR_LOG output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error. |
MC_FIPS_PERIODIC_ENABLE
Enables or disables periodic FIPS self-test. When enabled, the MCU runs FIPS self-tests in the background at a configurable interval (default: 60 seconds).
Command Code: 0x4D46_5045 ("MFPE")
Table: MC_FIPS_PERIODIC_ENABLE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over input data |
| enable | u32 | 0 = disable, 1 = enable periodic test |
Table: MC_FIPS_PERIODIC_ENABLE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error. |
MC_FIPS_PERIODIC_STATUS
Retrieves the status of the periodic FIPS self-test, including whether it is enabled, the number of completed iterations, and the result of the last test.
Command Code: 0x4D46_5053 ("MFPS")
Table: MC_FIPS_PERIODIC_STATUS input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over input data |
Table: MC_FIPS_PERIODIC_STATUS output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error. |
| enabled | u32 | 0 = disabled, 1 = enabled |
| iterations | u32 | Number of completed periodic test iterations |
| last_result | u32 | Last test result: 0 = not run yet, 1 = pass, 2 = fail |
MC_ECDSA384_SIG_VERIFY
Verifies an ECDSA P-384 signature. The hash to be verified is taken from the input.
Command Code: 0x4D45_4356 ("MECV")
Table: MC_ECDSA384_SIG_VERIFY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| pub_key_x | u8[48] | X portion of the ECDSA verification key. |
| pub_key_y | u8[48] | Y portion of the ECDSA verification key. |
| signature_r | u8[48] | R portion of the signature to verify. |
| signature_s | u8[48] | S portion of the signature to verify. |
| hash | u8[48] | SHA-384 digest to verify. |
Table: MC_ECDSA384_SIG_VERIFY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by responder. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
MC_LMS_SIG_VERIFY
Verifies an LMS signature. The hash to be verified is taken from the input.
Command Code: 0x4D4C_4D56 ("MLMV")
Table: MC_LMS_SIG_VERIFY input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| pub_key_tree_type | u8[4] | LMS public key algorithm type. Must equal 12. |
| pub_key_ots_type | u8[4] | LM-OTS algorithm type. Must equal 7. |
| pub_key_id | u8[16] | "I" Private key identifier |
| pub_key_digest | u8[24] | "T[1]" Public key hash value |
| signature_q | u8[4] | Leaf of the Merkle tree where the OTS public key appears |
| signature_ots | u8[1252] | LM-OTS signature |
| signature_tree_type | u8[4] | LMS signature Algorithm type. Must equal 12. |
| signature_tree_path | u8[360] | Path through the tree from the leaf associated with the LM-OTS signature to the root |
| hash | u8[48] | SHA384 digest to verify. |
Table: MC_LMS_SIG_VERIFY output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by MCU. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
MC_ECDSA384_SIGN
Requests to sign SHA-384 digest with DPE leaf cert.
Command Code: 0x4D45_4353 ("MECS")
Table: MC_ECDSA384_SIGN input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| digest | u8[48] | SHA-384 digest to be signed. |
Table: MC_ECDSA384_SIGN output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments, computed by MCU. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| derived_pubkey_x | u8[48] | The X BigNum of the ECDSA public key associated with the signing key. |
| derived_pubkey_y | u8[48] | The Y BigNum of the ECDSA public key associated with the signing key. |
| signature_r | u8[48] | The R BigNum of an ECDSA signature. |
| signature_s | u8[48] | The S BigNum of an ECDSA signature. |
MC_MLDSA_SIGN
Request to sign the SHA-384 digest with DPE leaf cert.
Command Code: 0x4D4C_4D53 ("MMLS")
Table: MC_MLDSA_SIGN input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. |
| digest | u8[48] | SHA-384 digest to be signed. |
Table: MC_MLDSA_SIGN output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| pub_key_tree_type | u8[4] | LMS public key algorithm type. |
| pub_key_ots_type | u8[4] | LM-OTS algorithm type. |
| pub_key_id | u8[16] | Private key identifier. |
| pub_key_digest | u8[24] | Public key hash value. |
| signature_q | u8[4] | Leaf of the Merkle tree for the OTS key. |
| signature_ots | u8[1252] | LM-OTS signature. |
| signature_tree_path | u8[360] | Path through the Merkle tree to the root. |
MC_PRODUCTION_DEBUG_UNLOCK_REQ
Requests debug unlock in production environment.
Command Code: 0x4D44_5552 ("MDUR")
Table: MC_PRODUCTION_DEBUG_UNLOCK_REQ input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| length | u32 | Length of the message in DWORDs |
| unlock_level | u8 | Debug unlock Level (Number 1-8) |
| reserved | u8[3] | Reserved field |
Table: MC_PRODUCTION_DEBUG_UNLOCK_REQ output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other output arguments. |
| fips_status | u32 | FIPS approved or an error |
| length | u32 | Length of the message in DWORDs. |
| unique_device_identifier | u8[32] | Device identifier of the Caliptra device. |
| challenge | u8[48] | Random number challenge. |
MC_PRODUCTION_DEBUG_UNLOCK_TOKEN
Sends the debug unlock token.
Command Code: 0x4D44_5554 ("MDUT")
Table: MC_PRODUCTION_DEBUG_UNLOCK_TOKEN input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | Checksum over other input arguments. |
| fips_status | u32 | FIPS approved or an error |
| length | u32 | Length of the message in DWORDs. |
| unique_device_identifier | u8[32] | Device identifier of the Caliptra device. |
| unlock_level | u8 | Debug unlock level (1-8). |
| reserved | u8[3] | Reserved field. |
| challenge | u8[48] | Random number challenge. |
| ecc_public_key | u32[24] | ECC public key in hardware format (little endian). |
| mldsa_public_key | u32[648] | MLDSA public key in hardware format (little endian). |
| ecc_signature | u32[24] | ECC P-384 signature of the message hashed using SHA2-384 (R and S coordinates). |
| mldsa_signature | u32[1157] | MLDSA signature of the message hashed using SHA2-512 (4627 bytes + 1 reserved byte). |
Table: MC_PRODUCTION_DEBUG_UNLOCK_TOKEN output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
MC_FUSE_READ
Reads fuse values.
Command Code: 0x4946_5052 ("IFPR")
Table: MC_FUSE_READ input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| partition | u32 | Partition number to read from |
| entry | u32 | Entry to read |
Table: MC_FUSE_READ output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| length (bits) | u32 | Number of bits that are valid |
| data | u8[...] | Fuse data (length/8) |
MC_FUSE_WRITE
Write fuse values.
Start bit is counting from the least significant bit.
Command Code: 0x4946_5057 ("IFPW")
Table: MC_FUSE_WRITE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| partition | u32 | Partition number to write to |
| entry | u32 | Entry to write |
| start bit | u32 | Starting bit to write to (least significant bit in entry is 0). |
| length | u32 | in bits |
| data | u8[...] | length/8 |
Table: MC_FUSE_WRITE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
Caveats:
- This command is idempotent, so that identical writes will have no effect.
- Will fail if any of the existing data is 1 but is set to 0 in the input data. Existing data that is 0 but set to 1 will be burned to a 1.
- Writes to buffered partitions will not take effect until the next reset.
MC_FUSE_LOCK_PARTITION
Lock a partition.
Command Code: 0x4946_504B ("IFPK")
Table: MC_FUSE_LOCK_PARTITION input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| partition | u32 | Partition number to lock |
Table: MC_FUSE_LOCK_PARTITION output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
Caveats:
- This command is idempotent, so that locking a partition twice has no effect.
- Locking a partition causes subsequent writes to it to fail.
- Locking does not fully take effect until the next reset.
Cryptographic Command Format
The MCI mailbox cryptographic commands are mapped to their corresponding Caliptra Mailbox Cryptographic commands. The mapping is detailed in the table below. For the specific format of each command, refer to the Mailbox Commands: Cryptographic Mailbox (2.0).
Table: mapping MCI Mailbox Crypto Commands to Caliptra Crypto Mailbox Commands
| MCI Mailbox Crypto Commands | Caliptra Mailbox Crypto Commands |
|---|---|
MC_FIPS_SELF_TEST_START | SELF_TEST_START |
MC_FIPS_SELF_TEST_GET_RESULTS | SELF_TEST_GET_RESULTS |
MC_SHA_INIT | CM_SHA_INIT |
MC_SHA_UPDATE | CM_SHA_UPDATE |
MC_SHA_FINAL | CM_SHA_FINAL |
MC_HMAC | CM_HMAC |
MC_HMAC_KDF_COUNTER | CM_HMAC_KDF_COUNTER |
MC_HKDF_EXTRACT | CM_HKDF_EXTRACT |
MC_HKDF_EXPAND | CM_HKDF_EXPAND |
MC_AES_ENCRYPT_INIT | CM_AES_ENCRYPT_INIT |
MC_AES_ENCRYPT_UPDATE | CM_AES_ENCRYPT_UPDATE |
MC_AES_DECRYPT_INIT | CM_AES_DECRYPT_INIT |
MC_AES_DECRYPT_UPDATE | CM_AES_DECRYPT_UPDATE |
MC_AES_GCM_ENCRYPT_INIT | CM_AES_GCM_ENCRYPT_INIT |
MC_AES_GCM_ENCRYPT_UPDATE | CM_AES_GCM_ENCRYPT_UPDATE |
MC_AES_GCM_ENCRYPT_FINAL | CM_AES_GCM_ENCRYPT_FINAL |
MC_AES_GCM_DECRYPT_INIT | CM_AES_GCM_DECRYPT_INIT |
MC_AES_GCM_DECRYPT_UPDATE | CM_AES_GCM_DECRYPT_UPDATE |
MC_AES_GCM_DECRYPT_FINAL | CM_AES_GCM_DECRYPT_FINAL |
MC_ECDH_GENERATE | CM_ECDH_GENERATE |
MC_ECDH_FINISH | CM_ECDH_FINISH |
MC_ECDSA_CMK_PUBLIC_KEY | CM_ECDSA_PUBLIC_KEY |
MC_ECDSA_CMK_SIGN | CM_ECDSA_SIGN |
MC_ECDSA_CMK_VERIFY | CM_ECDSA_VERIFY |
MC_RANDOM_STIR | CM_RANDOM_STIR |
MC_RANDOM_GENERATE | CM_RANDOM_GENERATE |
MC_IMPORT | CM_IMPORT |
MC_DELETE | CM_DELETE |
a6784b6
External MCTP VDM Commands Spec
Overview
This document specifies the external command protocol used by the Baseboard Management Controller (BMC) to communicate with the device integrating the Caliptra RoT subsystem for querying device-specific information, retrieving debug logs and attestation logs, managing certificates and secure debug unlock etc. The protocol is based on the MCTP (Management Component Transport Protocol) over the I3C interface and uses a vendor-defined message type (0x7E).
-
Device Identification and Capabilities
- Retrieve firmware versions, unique device identifiers, and device capabilities to ensure compatibility and proper configuration.
- Query device-specific information such as chip identifiers or subsystem details.
-
Debugging and Diagnostics
- Retrieve debug logs to analyze device behavior, diagnose issues, and monitor runtime states.
- Clear logs to reset diagnostic data and maintain storage efficiency.
-
Certificate Management
- Export Certificate Signing Requests (CSRs) for device keys to facilitate secure provisioning.
- Import signed certificates to establish a trusted certificate chain for device authentication.
-
Debug Unlock Mechanisms
- Facilitate secure debugging in production environments
- Ensure controlled access to debugging features
Protocol
- Transport Layer: MCTP
- Message Type: The message type is
0x7Eas per the MCTP Base Specification. This message type supports Vendor Defined Messages, where the vendor is identified by the PCI-based Vendor ID. The initial message header is specified in the MCTP Base Specification and detailed below for completeness:
| Field Name | Byte(s) | Description |
|---|---|---|
| Request Data | ||
| PCI/PCIe Vendor ID | 1:2 | The MCTP Vendor ID formatted per 00h Vendor ID format offset. |
| Vendor-Defined Message Body | 3:N | Vendor-defined message body, 0 to N bytes. |
| Response Data | ||
| PCI/PCIe Vendor ID | 1:2 | The value is formatted per 00h Vendor ID offset. |
| Vendor-Defined Message Body | 3:M | Vendor-defined message body, 0 to M bytes. |
The Vendor ID is a 16-bit unsigned integer, described in the PCI 2.3 specification. The value identifies the device manufacturer. The message body and content are described in the sections below.
Message Format
This section describes the MCTP message format used to support Caliptra subsystem external command protocol. The request/response message body encapsulates the Vendor Defined MCTP message within the MCTP transport. Details of MCTP message encapsulation can be found in the MCTP Base Specification. The MCTP Get Vendor Defined Message Support command allows discovery of the vendor-defined messages supported by an endpoint. This discovery process identifies the vendor organization and the supported message types. The format of this request is specified in the MCTP Base Specification.
For the Caliptra external command protocol, the following information is returned in response to the MCTP Get Vendor Defined Message Support request:
- Vendor ID Format:
0 - PCI Vendor ID:
0x1414 - Command Set Version:
4
The following table provides detailed descriptions of the fields used in the Caliptra external command protocol:
| Field Name | Description |
|---|---|
| IC | (MCTP Integrity Check bit) Indicates whether the MCTP message is covered by an overall MCTP message payload integrity check. |
| Message Type | Indicates an MCTP Vendor Defined Message. |
| MCTP PCI Vendor | ID for PCI Vendor. Caliptra messages use the Microsoft PCI ID of 0x1414. |
| Request Type | Distinguishes between request and response messages: set to 1 for requests, and 0 for responses. |
| Crypt | Indicates whether the Message Payload and Command are encrypted. |
| Command Code | The command ID for the operation to execute. |
| Msg Integrity Check | Represents the optional presence of a message type-specific integrity check over the contents of the message body. If present (indicated by the IC bit), the Message Integrity Check field is carried in the last bytes of the message body. |
The following table describes the MCTP message format used in the Caliptra external command protocol:
Table: MCTP Vendor Defined Message Format
The protocol header fields are to be included only in the first packet of a multiple-packet MCTP message. After reconstruction of the message body, the protocol header will be used to interpret the message contents. Reserved fields must be set to 0.
Command List
The following table describes the commands defined under this specification. There are two categories: (1) Required commands (R) that are mandatory for all implementations, (2) Optional commands (O) that may be utilized if the specific implementation requires it.
| Message Name | Command | R/O | Description |
|---|---|---|---|
| Firmware Version | 01h | R | Retrieve firmware version information. |
| Device Capabilities | 02h | R | Retrieve device capabilities. |
| Device ID | 03h | R | Retrieve device ID. |
| Device Information | 04h | R | Retrieve device information. |
| Export CSR | 05h | R | Export CSR for device keys. |
| Import Certificate | 06h | R | Import CA-signed certificate. |
| Get Certificate State | 07h | R | Check the state of the signed certificate chain. |
| Get Log | 08h | R | Retrieve debug log or attestation log. |
| Clear Log | 09h | R | Clear log information. |
| Request Debug Unlock | 0Ah | O | Request debug unlock in production environment. |
| Authorize Debug Unlock Token | 0Bh | O | Send debug unlock token to device for authorization. |
Command Format
This section defines the structure of the Message Payload field, as referenced in the "MCTP Vendor Defined Message Format" table for each command's request and response messages.
Firmware Version
Retrieves the version of the target firmware.
Request Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | area_index | u32 | Area Index: - 00h = Caliptra core firmware - 01h = MCU runtime firmware - 02h = SoC firmware Additional indexes are firmware-specific |
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
| 4:35 | version | u8[32] | Firmware Version Number in ASCII format |
Device Capabilities
Request Payload: Empty
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
| 4:35 | caps | u8[32] | Device Capabilities: - Bytes [0:7]: Reserved for Caliptra RT - Bytes [8:11]: Reserved for Caliptra FMC - Bytes [12:15]: Reserved for Caliptra ROM - Bytes [16:23]: Reserved for MCU RT - Bytes [24:27]: Reserved for MCU ROM - Bytes [28:31]: Reserved |
Device ID
This command retrieves the device ID. The request for this command contains no additional payload.
Request Payload: Empty
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
| 4:5 | vendor_id | u16 | Vendor ID; LSB |
| 6:7 | device_id | u16 | Device ID; LSB |
| 8:9 | subsystem_vendor_id | u16 | Subsystem Vendor ID; LSB |
| 10:11 | subsystem_id | u16 | Subsystem ID; LSB |
Device Information
This command retrieves information about the target device.
Request Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | info_index | u32 | Information Index: - 00h = Unique Chip Identifier Additional indexes are firmware-specific |
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
| 4:7 | data_size | u32 | Size of the requested data in bytes |
| 8:N | data | u8[data_size] | Requested information in binary format |
Export CSR
Exports the IDEVID Self-Signed Certificate Signing Request.
Request Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | index | u32 | Index: Default = 0 - 00h = IDEVID ECC CSR - 01h = IDEVID MLDSA CSR |
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
| 4:7 | data_size | u32 | Length in bytes of the valid data in the data field |
| 8:N | data | u8[data_size] | DER-encoded IDevID certificate signing request |
Import Certificate
Allows SoC to import DER-encoded IDevId certificate on every boot. The IDevId certificate is added to the start of the certificate chain.
Request Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | cert_size | u32 | Size of the DER-encoded IDevID certificate. |
| 4:N | cert | u8[cert_size] | DER-encoded certificate |
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
Get Certificate State
Determines the state of the certificate chain for signed certificates that have been sent to the device. The request for this command contains no additional payload.
Request Payload: Empty
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
| 4:7 | state | u32 | State: - 0 = A valid chain has been provisioned. - 1 = A valid chain has not been provisioned. - 2 = The stored chain is being validated. |
| 8:11 | error_details | u32 | Error details if chain validation has failed. |
Get Log
Retrieves the internal log for the RoT. There are two types of logs available: the Debug Log, which contains RoT application information and machine state and the Attestation Measurement Log, which is similar to the TCG log.
Table: log types
| Log Type | Description |
|---|---|
| 0 | Debug Log |
| 1 | Attestation Log |
Request Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | log_type | u32 | Type of log to retrieve - 0 = Debug Log - 1 = Attestation Log |
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
| 4:7 | data_size | u32 | Size of the log data in bytes |
| 8:N | data | u8[data_size] | Log contents |
The length is determined by the end of the log or the packet size based on device capabilities. If the response spans multiple MCTP messages, the end of the response will be determined by an MCTP message with a payload smaller than the maximum payload supported by both devices. To guarantee a response will never fall exactly on the max payload boundary, the responder must send back an extra packet with zero payload.
Debug Log Format:
The debug log reported by the device has no specified format, as this can vary between different devices and is not necessary for attestation. It is expected that diagnostic utilities for the device will be able to understand the exposed log information. A recommended entry format is provided here:
| Offset | Description |
|---|---|
| 1:7 | Log Entry Header |
| 8:9 | Format of the entry (e.g., 1 for current format) |
| 10 | Severity of the entry |
| 11 | Identifier for the component that generated the message |
| 12 | Identifier for the entry message |
| 13:16 | Message-specific argument |
| 17:20 | Message-specific argument |
Clear Log
Clears the log in the RoT subsystem.
Request Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | log_type | u32 | Log Type: - 0 = Debug Log - 1 = Attestation Log |
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
Request Debug Unlock
Requests debug unlock in production environment.
Request Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | length | u32 | Length of the message in DWORDs |
| 4 | unlock_level | u8 | Debug unlock level (1-8) |
| 5:7 | reserved | u8[3] | Reserved field |
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
| 4:7 | length | u32 | Length of the message in DWORDs |
| 8:39 | unique_device_identifier | u8[32] | Device identifier of the Caliptra device |
| 40:87 | challenge | u8[48] | Random number challenge |
Authorize Debug Unlock Token
Authorizes the debug unlock token.
Request Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | length | u32 | Length of the message in DWORDs |
| 4:35 | unique_device_identifier | u8[32] | Device identifier of the Caliptra device |
| 36 | unlock_level | u8 | Debug unlock level (1-8) |
| 37:39 | reserved | u8[3] | Reserved field |
| 40:87 | challenge | u8[48] | Random number challenge |
| 88:183 | ecc_public_key | u32[24] | ECC public key in hardware format (little endian) |
| 184:2635 | mldsa_public_key | u32[648] | MLDSA public key in hardware format (little endian) |
| 2636:2731 | ecc_signature | u32[24] | ECC P-384 signature of the message hashed using SHA2-384 (R and S coordinates) |
| 2732:6195 | mldsa_signature | u32[1157] | MLDSA signature of the message hashed using SHA2-512 (4627 bytes + 1 reserved byte) |
Response Payload:
| Byte(s) | Name | Type | Description |
|---|---|---|---|
| 0:3 | completion_code | u32 | Command completion status |
a6784b6
Unified Handling of External Commands
The Caliptra MCU firmware provides two external command interfaces: MCTP VDM external commands and MCI Mailbox commands. Although these interfaces operate over different protocols, they deliver overlapping functionality to external clients.
Table: Overlapping commands between MCTP VDM and MCI mailbox| MCTP VDM Command | MCI Mailbox Command | Description |
|---|---|---|
| Firmware Version | MC_FIRMWARE_VERSION | Retrieves the version of the firmware. |
| Device Capabilities | MC_DEVICE_CAPABILITIES | Retrieves device capabilities. |
| Device ID | MC_DEVICE_ID | Retrieves the device ID. |
| Device Information | MC_DEVICE_INFO | Retrieves device information. |
| Export CSR | MC_EXPORT_IDEV_CSR | Exports the IDEVID CSR. |
| Import Certificate | MC_IMPORT_IDEV_CERT | Imports the IDevID certificate. |
| Get Log | MC_GET_LOG | Retrieves the internal log. |
| Clear Log | MC_CLEAR_LOG | Clears the log in the RoT subsystem. |
| Request Debug Unlock | MC_PRODUCTION_DEBUG_UNLOCK_REQ | Requests debug unlock in a production environment. |
| Authorize Debug Unlock Token | MC_PRODUCTION_DEBUG_UNLOCK_TOKEN | Sends the debug unlock token for authorization. |
To ensure consistent command behavior and maximize code reuse, we define a protocol-agnostic command handler trait with unified command IDs and input/output types. Both MCTP VDM and MCI mailbox frontends parse their protocol, map to the unified command and call the same backend handler, ensuring code reuse and consistent behavior.
- Architecture
flowchart TD
A0[Application]
%% Protocol-specific entry points
A1[MCTP VDM Service API]
A2[MCI Mailbox Service API]
%% Protocol-specific routers/parsers
B1[MCTP VDM Parser</br>Encoding/Decoding </br>Router Protocol specific]
B2[MCI Mailbox Parser and Router Protocol specific]
%% Protocol-agnostic handler
C[Common Command Handler
Protocol agnostic]
%% Device APIs
D2[Logging API]
D3[Caliptra Mailbox API]
D4[Keystore API]
%% Flow connections
A0 --> A1
A0 --> A2
A1 --> B1
A2 --> B2
B1 --> C
B2 --> C
C --> D2
C --> D3
C --> D4
C --> ..
%% Layer grouping
subgraph Protocol_Specific["Protocol-Specific Layer"]
B1
B2
end
subgraph Protocol_Agnostic["Protocol-Agnostic Layer"]
C
end
subgraph Device_APIs["Other Service API Layer"]
D2
D3
D4
..
end
- Interface
/// A trait for handling protocol-agnostic commands asynchronously.
///
/// Implementors of this trait can process commands identified by [`UnifiedCommandId`],
/// using the provided input and output buffers, and return a [`Result`] indicating
/// success or a [`CommandError`].
#[async_trait]
pub trait UnifiedCommandHandler {
/// Handles a unified command asynchronously.
///
/// # Arguments
///
/// * `command` - The identifier of the command to handle.
/// * `input` - The input data and protocol information for the command.
/// * `output` - The output buffer and protocol information for the command response.
///
/// # Returns
///
/// * `Ok(())` if the command was handled successfully.
/// * `Err(CommandError)` if an error occurred during command handling.
async fn handle_command<'a>(
&self,
command: UnifiedCommandId,
input: CommandInput<'a>,
output: CommandOutput<'a>,
) -> Result<(), CommandError>;
}
/// Adapter for handling MCTP VDM protocol commands using a UnifiedCommandHandler.
///
/// This struct parses MCTP VDM messages, maps them to unified commands, and delegates
/// handling to the provided UnifiedCommandHandler implementation. The same buffer is
/// used for both request and response payloads.
pub struct MctpVdmAdapter<H: UnifiedCommandHandler + Send + Sync> {
handler: H,
}
impl<H: UnifiedCommandHandler + Send + Sync> MctpVdmAdapter<H> {
/// Creates a new MctpVdmAdapter with the given unified command handler.
pub fn new(handler: H) -> Self {
Self { handler }
}
/// Handles an incoming MCTP VDM message asynchronously.
///
/// Parses the message, maps it to a UnifiedCommandId, and invokes the handler.
/// The same buffer is used for both request and response.
/// Returns the response length or an error.
pub async fn handle_vdm_message<'a>(
&self,
buf: &'a mut [u8],
req_len: usize,
) -> Result<usize, CommandError> {
}
}
/// Identifiers for all supported unified commands.
///
/// Each variant represents a distinct command that can be handled by a [`UnifiedCommandHandler`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UnifiedCommandId {
/// Query the firmware version.
FirmwareVersion,
/// Query the device capabilities.
DeviceCapabilities,
/// Query the device ID.
DeviceId,
/// Query device information.
DeviceInformation,
/// Export a Certificate Signing Request (CSR).
ExportCsr,
/// Import a certificate.
ImportCertificate,
/// Query the state of a certificate.
GetCertificateState,
/// Retrieve device logs.
GetLog,
/// Clear device logs.
ClearLog,
// ... add more as needed
}
/// Input data and protocol information for a unified command.
///
/// The input buffer is provided as a byte slice, along with the protocol used.
pub struct CommandInput<'a> {
/// Input data buffer.
pub data: &'a [u8],
/// Protocol used for the command.
pub protocol: CommandProtocol,
}
/// Output data and protocol information for a unified command.
///
/// The output buffer is provided as a mutable byte slice, along with the protocol used
/// and the length of the valid output data.
pub struct CommandOutput<'a> {
/// Output data buffer.
pub data: &'a mut [u8],
/// Protocol used for the command.
pub protocol: CommandProtocol,
/// Length of valid data written to the output buffer.
pub len: usize,
}
/// Supported protocols for unified commands.
///
/// Indicates the protocol over which the command is received or sent.
#[derive(Debug, Clone, Copy)]
pub enum CommandProtocol {
/// Management Component Transport Protocol (MCTP).
Mctp,
/// Management Controller Interface Mailbox (MCI MBX).
MciMbx,
}
/// Errors that can occur during unified command handling.
///
/// Used as the error type in [`UnifiedCommandHandler::handle_command`].
#[derive(Debug)]
pub enum CommandError {
/// The command is not recognized or supported.
InvalidCommand,
/// The input data is invalid or malformed.
InvalidInput,
/// An internal error occurred during command handling.
InternalError,
/// The command is not supported in the current context.
NotSupported,
/// The handler is busy and cannot process the command at this time.
Busy,
// Protocol-specific errors can be added here
}
a6784b6
Logging Stack
Overview
This document describes the design of the logging stack for Caliptra MCU firmware. The logging stack provides a unified, extensible and robust mechanism for capturing, storing, and retrieving logs from both kernel/driver and userspace components. It supports multiple backends (RAM, flash, etc.), multiple log types and safe concurrent access.
Generic Log Entry Format
Each log entry consists of a fixed-size header followed by variable-length data. The header provides metadata about the entry, while the data contains the log-specific payload.
Table: generic log entry header
| Field Name | Type | Size (bytes) | Description |
|---|---|---|---|
| log_magic | u16 | 2 | Start of entry marker |
| length | u16 | 2 | Total length of the entry (header + data) |
| entry_id | u32 | 4 | Unique identifier for the log entry |
Table: generic log entry
| Field Name | Type | Size (bytes) | Description |
|---|---|---|---|
| log_magic | u16 | 2 | Start of entry marker |
| length | u16 | 2 | Total length of the entry (header + data) |
| entry_id | u32 | 4 | Unique identifier for the log entry |
| data | [u8] | variable | Log entry payload |
The data field may include additional structured information (such as severity, component, message index, timestamp) depending on the log type.
Reference Format - Debug Log Entry Data
Table: Reference DebugLogEntryData fields
| Field Name | Type | Size (bytes) | Description |
|---|---|---|---|
| format | u16 | 2 | Format of the log entry |
| severity | u8 | 1 | Severity level of the entry (error, info, warning, etc.) |
| component | u8 | 1 | System component that generated the entry |
| msg_index | u8 | 1 | Identifier for the entry message |
| arg1 | u32 | 4 | Message-specific argument |
| arg2 | u32 | 4 | Message-specific argument |
| cycle_count | u64 | 8 | Elapsed processor cycles since boot. Note: This is a raw cycle count for performance reasons. Converting to milliseconds requires dividing by the clock frequency, which can be done offline or by the log consumer if needed. |
All log entries are stored as a contiguous byte array in the backend storage.
Backend Logging Storages
-
Flash Backend
- Stores log entries in a dedicated flash partition.
- Ensures persistence across resets and power cycles.
- Suitable for audit, crash, and security logs.
- Partitioning is enforced using a flash partition capsule.
-
RAM Backend
- Implements a circular buffer in RAM.
- Provides fast access, but is volatile (logs are lost on reset).
- Primarily used for debug and runtime diagnostics.
-
Multiple Log Types
- Each log type (e.g., debug, TCG) can use a separate backend instance or partition.
- The syscall driver and API accept a log type parameter to route requests to the appropriate backend.
Architecture
The logging module leverages the Tock Kernel Log HIL to provide a unified and extensible logging API. It supports multiple storage backends, with initial implementations for both flash-based and RAM-based storage in MCU SDK.
flowchart TD
A[Userspace App]
B[Logging API]
C[Logging Syscall Driver]
subgraph Capsule Layer
D1[Log Capsule RAM]
D2[Log Capsule Flash<br/>capsules/extra/src/log.rs]
end
F1[hil::flash]
F2[hil::log]
subgraph Log Storage Backend
G1[Volatile Memory RAM]
G2[Flash Ctrl]
end
%% Show vertical stacking by linking invisible nodes
A -.-> B
B -.-> C
C -.-> D1
C -.-> D2
D1 -.-> F2
D2 -.-> F1
D2 -.-> F2
F1 -.-> G2
D1 -.-> G1
Key Components
- Logging API: provides a high-level, backend-agnostic interface for application to use logging module.
- Logging Syscall Driver: bridges userspace and kernel space, exposing logging operations to applications via Tock syscalls.
- Log Capsules: implement the
kernel::hil::loginterface for each backend storage type. Tock provides a capsulecapsules/extra/src/log.rsfor flash-based log storage, which can be used as-is. To add RAM support, a new capsule implementing the same HIL interface should be created. This modular design enables easy integration of new storage backends by simply implementing the required HIL and capsule, without modifying the core logging logic. - Tock Kernel Log HIL: provides the abstraction for logging operations.
Logging API
The logging API is defined by the Logger trait, which provides a uniform interface for log creation, flushing, clearing, size querying, and reading log contents.
#![allow(unused)] fn main() { /// Defines the API for logging information. pub trait Logger { /// Add a new entry to the log. /// /// # Arguments /// * `entry` - A byte slice containing the log entry data (including header and payload). /// /// # Returns /// * `Ok(())` if the entry was successfully added. /// * `Err(LoggingError)` if the operation failed (e.g., insufficient space). fn create_entry(&mut self, entry: &[u8]) -> Result<(), LoggingError>; /// Flush the internal buffers to the underlying storage. /// /// Ensures that any buffered log data is written to persistent storage (if applicable). /// /// # Returns /// * `Ok(())` if the flush was successful. /// * `Err(LoggingError)` if the operation failed. fn flush(&mut self) -> Result<(), LoggingError>; /// Remove all data from the log. /// /// Clears all log entries from the backend storage. /// /// # Returns /// * `Ok(())` if the log was successfully cleared. /// * `Err(LoggingError)` if the operation failed. fn clear(&mut self) -> Result<(), LoggingError>; /// Get the amount of data currently stored in the log (including headers). /// /// # Returns /// * `Ok(usize)` with the total number of bytes currently stored. /// * `Err(LoggingError)` if the operation failed. fn get_size(&self) -> Result<usize, LoggingError>; /// Get the current contents of the log (raw log data, including headers). /// /// Copies up to `buffer.len()` bytes of log data starting from `offset` into `buffer`. /// /// # Arguments /// * `offset` - The starting byte offset within the log. /// * `buffer` - The buffer to fill with log data. /// /// # Returns /// * `Ok(usize)` with the number of bytes copied into the buffer. /// * `Err(LoggingError)` if the operation failed. fn read_contents(&self, offset: usize, buffer: &mut [u8]) -> Result<usize, LoggingError>; } }
This trait is implemented by all log backends (RAM, flash, etc.) and is used by both kernel/driver code and userspace via the syscall driver.
Syscall Driver
Below is an example skeleton of a syscall driver implementation. This driver translates userspace commands into calls to the appropriate methods of the Logger trait.
#![allow(unused)] fn main() { pub const DRIVER_NUM: usize = 0xB0000; // Example driver number pub struct LogSyscallDriver<'a> { logger: &'a dyn Logger, grant: Grant<LogGrantData>, // Other fields } pub struct LogGrantData { // Per-process state if needed } impl<'a> LogSyscallDriver<'a> { pub fn new(logger: &'a dyn Logger, grant: Grant<LogGrantData>) -> Self { LogSyscallDriver { logger, grant } } } impl<'a> SyscallDriver for LogSyscallDriver<'a> { fn command( &self, command_num: usize, arg1: usize, arg2: usize, process: AppId, ) -> CommandReturn { match command_num { 0 => { /* create_entry */ } 1 => { /* flush */ } 2 => { /* clear */ } 3 => { /* get_size */ } 4 => { /* read_contents (not supported in this example) */ } _ => { /* unknown command */ } } } } }
Permission Control
Permission control restricts log access to authorized components at both kernel and userspace levels:
-
Kernel/Driver Level: Only trusted kernel capsules and drivers can access log backends. Privileged kernel code instantiates log capsules, which validate caller identity for sensitive operations.
-
Userspace Level: The syscall driver enforces per-process, per-log-type access policies (read-only, write-only, full, or no access) defined in kernel configuration. Each syscall is checked against these policies; unauthorized requests are rejected.
Permission Check Flow:
- Userspace issues a syscall.
- Driver retrieves process identity and log type.
- Permissions are checked.
- If allowed, the operation proceeds; otherwise, an error is returned.
This ensures only authorized access and modification of log data.
Kernel/Driver and Userspace Usage
- Kernel/Driver calls the
Loggertrait methods directly on the log capsule or syscall driver. - Userspace issues syscalls that forwards the requests to corresponding log capsules.
a6784b6
In-Field Fuse Programming (IFP) Specification
MCU has the OpenTitan fuse controller (OTP) and Lifecycle controller (LC), which sit on top of a process-specific fuse macro and manages them with a uniform interface.
The hardware has provisioned the OTP controller with an overall structure that has Caliptra- and general Subsystem-specific blocks, which are required, and vendor-defined blocks.
As part of the Fuse API, we support defining the vendor-specific fuses and assist in provisioning the Caliptra- and vendor-specific fuses.
In general, the Fuse API gives the SoC an easy way to read and write fuses with bit granularity via MCTP or mailbox commands.
Fuse Defining
We provide a new fuse definition hjson file format to assist in further defining fuses.
There are two places where we expand on the standard OTP memory map:
- Defining how many bits are backed by actual fuses in each field
- Defining vendor-specific fields
For an example of (1), the CPTRA_SS_OWNER_ECC_REVOCATION is specified as 1 byte (8 bits) in the memory map, but the may only have 4 bits backed by actual fuses in hardware. Our additional fuse map contains a section to define how many bits are available in each field as this information is vendor-specific and hence not present in the standard memory map.
For (2), there are two defined partitions, VENDOR_SECRET_PROD_PARTITION (520 bits) and VENDOR_NON_SECRET_PROD_PARTITION (984 bits) in the standard memory map. In addition, the vendor can choose to provide additional fuses. We provide a way to split these up into specific areas and automatically generate Rust code to manage them.
Fuse definition file
Here is an example fuse definition file, for example, vendor_fuses.hjson:
{
// vendor-specific secret fuses
secret_vendor: [
{"example_key1": 48}, // size in bytes
{"example_key2": 48}, // size in bytes
{"example_key3": 48}, // size in bytes
{"example_key4": 48}, // size in bytes
],
// vendor-specific non-secret-fuses
non_secret_vendor: [
{"example_key_revocation": 1}
],
// TBD how we allow additional fuses outside of these areas, if this is allowed by OTP
other_fuses: {},
// entries to define how many bits are in each field, and potentially other information
fields: [
// set specifics on Subsystem fuses
// By default, all bits in each field are assumed to be backed by actual fuse bits.
// Names should be globally unique
{name: "CPTRA_SS_OWNER_ECC_REVOCATION", bits: 4}, // size in bits
// set specifics on vendor-specific fuses
{name: "example_key_revocation", bits: 4},
]
}
By default, all bits in each field are assumed to be backed by actual fuse bits unless they have an entry in the fields array.
Fuse definition script
We will provide a script, cargo xtask fuse-autogen, that can be used to process an .hjson file into:
- Firmware definitions and code for the fuses and bits (Rust)
- Documentation on the fuses and bits (Markdown)
The command will use either a --platform flag to indicate the file is platforms/{platform}/fuse_bits.hjson or a --file to specify the file manually.
This script can optionally generate extra commands for programming specific fuses as well as Rust and C code to access them.
Fuse Provisioning Commands
The fuse provisioning commands can be used over our generic command interface through MCTP, mailboxes, etc., or through commands in firmware manifest (except read operations).
The commands are:
- Write
- Read
- Lock partition
Authorization
Only authorized SoC users shall call these commands. This can be authorized through the mechanism itself (i.e., a mailbox that only a trusted SoC component has access to) or cryptographically.
Some example cryptographic mechanisms that could be used:
- A certificate provided to MCU during boot, which will be used to validate signatures on each message.
- An HMAC key imported into Caliptra's cryptographic mailbox during boot, which MCU can use to validate a request. (The key could itself be stored as fuses.)
It will be integrator-defined how which authorization mechanism is required, but we will provide reference implementations with stubs for source-based and HMAC-based authorization.
These commands reference partition, fuse, and bit numbers that must use the same numbers generated from the Fuse definition script.
Limitations
- Reading is not allowed for secret partitions. The secret partitions can only be read via integrator-specific hardware (or by Caliptra for its secret partitions).
- Writing and locking are idempotent.
- Once a partition is locked, it cannot be written to again.
- u32s are represented in little-endian byte order
- Bytes are expected to be implemented in fuses in standard (big-endian) bit order, i.e., 76543210.
- If a byte is only partially filled with bits, then the high-order bits will be 0.
- Partitions, fields, and bits are specified in the particular platform's
otp_mmap.hjsonandvendor_fuses.hjsonfiles.
MC_FUSE_READ
Reads fuse values.
Command Code: 0x4946_5052 ("IFPR")
Table: MC_FUSE_READ input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| partition | u32 | Partition number to read from |
| entry | u32 | Entry to read |
Table: MC_FUSE_READ output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
| length (bits) | u32 | Number of bits that are valid |
| data | u8[...] | Fuse data (length/8) |
MC_FUSE_WRITE
Write fuse values.
Start bit is counting from the least significant bit.
Command Code: 0x4946_5057 ("IFPW")
Table: MC_FUSE_WRITE input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| partition | u32 | Partition number to write to |
| entry | u32 | Entry to write |
| start bit | u32 | Starting bit to write to (least significant bit in entry is 0). |
| length | u32 | in bits |
| data | u8[...] | length/8 |
Table: MC_FUSE_WRITE output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
Caveats:
- This command is idempotent, so that identical writes will have no effect.
- Will fail if any of the existing data is 1 but is set to 0 in the input data. Existing data that is 0 but set to 1 will be burned to a 1.
- Writes to buffered partitions will not take effect until the next reset.
MC_FUSE_LOCK_PARTITION
Lock a partition.
Command Code: 0x4946_504B ("IFPK")
Table: MC_FUSE_LOCK_PARTITION input arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| partition | u32 | Partition number to lock |
Table: MC_FUSE_LOCK_PARTITION output arguments
| Name | Type | Description |
|---|---|---|
| chksum | u32 | |
| fips_status | u32 | FIPS approved or an error |
Caveats:
- This command is idempotent, so that locking a partition twice has no effect.
- Locking a partition causes subsequent writes to it to fail.
- Locking does not fully take effect until the next reset.
Field Entropy Provisioning
Field entropy fuses can only be burned by Caliptra runtime, so the flow for provisioning them is different and any changes in them will invalidate any OCP Device Ownership Transfer blob stored on the device. See the MCU OCP DOT implementation spec.
This will be an MCU mailbox API command (or other signal to MCU runtime) to trigger that MCU should use the Caliptra mailbox PROGRAM_FIELD_ENTROPY command. If a mailbox command is used, it must be authorized in the same way as other fuse programming API commands (see authorization)
If OCP DOT is being used, then the MCU ROM must also update the DOT blob after Caliptra core burns field entropy. Failure to do so may "brick" a device or require a DOT recovery flow, as the ownership information could be corrupted after provisioning field entropy if the DOT root key is derived from the LDevID CDI.
The flow will be:
- MCU runtime receives a MCU_PROGRAM_FIELD_ENTROPY command through a mailbox or other signal.
- If DOT is enabled and the DOT root key is derived from LDevID, we need to store the DOT blob (and re-sign it after FE is updated):
- MCU runtime stores the current DOT blob in non-volatile storage, e.g., SRAM that won't be cleared on reset
- MCU runtime verifies this DOT blob using the current DOT root key.
- MCU runtime indicates that the DOT blob must be checked and re-derived by setting a register or other signal in non-volatile storage, for example, a generic output wire or SRAM.
- Sign the DOT blob copy with a new special DOT root key derived from IDevID.
- MCU runtime sends to Caliptra's runtime mailbox the PROGRAM_FIELD_ENTROPY command.
- MCU runtime waits for a successful response.
- MCU runtime initiates a reset.
- If DOT is enabled and the DOT root key is derived from LDevID
- MCU ROM checks if we are in a FE programming mode (i.e., from register, input wires, or SRAM contents)
- MCU derives both special DOT root key (from IDevID) and the normal DOT root key (from LDevID)
- MCU ROM verifies the DOT blob copy in non-volatile storage against the special IDevID DOT root key.
- MCU ROM copies the DOT blob copy into the standard DOT blob location
- MCU ROM asks Caliptra core signs the DOT blob with the current DOT root key.
- Continue with the normal boot flow
sequenceDiagram Participant SoC Note over MCU,Caliptra: Runtime SoC->>MCU: MCU_PROGRAM_FIELD_ENTROPY alt DOT enabled and LDevID DOT key used MCU->>Caliptra: Derive special DOT key MCU->>Caliptra: HMAC DOT blob with special DOT key end MCU->>Caliptra: PROGRAM_FIELD_ENTROPY Caliptra->>OTP: (Burn) Caliptra->>MCU: Done MCU->>SoC: Reset subsystem Note over MCU,Caliptra: ROM alt DOT enabled and LDevID DOT key used MCU->>Caliptra: Derive DOT normal and special key MCU->>Caliptra: HMAC DOT blob with special key MCU->>MCU: verify HMAC MCU->>Caliptra: HMAC DOT blob with normal key end
Fuse Layout Options
The firmware uses a FuseLayout enum to define how fuse values are stored and interpreted. This provides flexibility for different encoding schemes including redundancy and error tolerance. The layout policy can be customized per platform via McuFuseLayoutPolicy.
Note that u32s (dwords) are always packed in little-endian byte order and big-endian bit order.
Layout Types
Single
Values are stored literally without any encoding.
Values stored this way in fuses should be protected with ECC.
Example: A 4-bit value 0b1101 is stored as 0b1101
OneHot
The logical value is the count of bits set to 1 in the fuse field. This is commonly used for version numbers and counters where incrementing requires only burning one additional fuse.
The values can span multiple u32 words but the result is always a single u32 count value.
Example:
0b0000β 00b0111β 3
Caution: This method of storing fuses is dangerous, since generally ECC cannot be used if the value can be incremented after the initial value is written.
LinearMajorityVote
Each logical bit is duplicated multiple times within a single u32 (or across adjacent u32s). The final value uses majority voting for each bit position to provide error tolerance.
Duplication is limited to < 32x and should be odd.
Example: With 2-bit logical value and 3x duplication:
- Raw fuses:
0b100_110_111(bit 0 has votes111, bit 1 has votes110, bit 2 has votes100) - Result:
0b011
Generally this method is used for values that do not have ECC protection but are only written once, usually that only are a few bits in size or less.
OneHotLinearMajorityVote
Combines LinearMajorityVote with OneHot encoding. First applies majority voting to each duplicated bit, then counts the number of bits set.
This is the recommended way to
Example: With 3 logical bits and 3x duplication:
- Raw fuses:
0b100_110_111(bit 0 has votes111, bit 1 has votes110, bit 2 has votes100) - After majority vote on each bit:
0b011(2 bits set) - Result: 2
Generally this method is used for counters and version numbers that can be updated in production and therefore do not have ECC protection.
WordMajorityVote
Entire u32 words are duplicated. Each bit position across the duplicated words is decided by majority vote.
Example: With 3 duplicated words:
- Raw fuses:
[0b100, 0b110, 0b111] - Bit 0: votes are
0,0,1β majority is0 - Bit 1: votes are
0,1,1β majority is1 - Bit 2: votes are
1,1,1β majority is1 - Result:
[0b110]
This is an alternative to LinearMajorityVote for larger values.
Common Limitations
- Maximum result size for single value extraction is 32 bits
- For multi-word extraction, the result array size must match the expected output
- All layouts return
McuError::ROM_FUSE_LAYOUT_TOO_LARGEif constraints are violated - Unsupported or invalid configurations return
McuError::ROM_UNSUPPORTED_FUSE_LAYOUT - Majority vote calculations use ceiling division (e.g., need β₯2 votes for 3 duplicates)
Default Platform Configuration
The default McuFuseLayoutPolicy uses:
Single: For certificate data, identifiers, and validity flags that are assumed to be protected by ECCOneHotLinearMajorityVote(3x): For SVN fields and countersLinearMajorityVote(3x): for single revocation fieldsWordMajorityVote(3x): for revocation bitmasks
This provides a balance between redundancy for critical security fields and storage efficiency for larger data structures.
a6784b6
Firmware Bundler
Overview
The firmware-bundler package is a library for unifying and simplifying the process for building rom and runtime bundles of the subsystem applications for deployment. As such the bundler is composed of 3 primary functionalities:
- Generate Linker Scripts - Using a provided Manifest generate linker scripts for the various applications
- Execute binary Build - Compile the binaries with
rustcand the linker script generated for a binary - Bundle Binaries - Bundle the tockOS runtime applications into a single binary blob for deployment.
The firmware-bundler can either be used directly by platforms within the caliptra-mcu-sw repository, or integrated with a Vendor's Out of Tree repository via an xtask extension to Cargo.
Build Patterns
The firmware-bundler supports 2 methods of determining the memory allocation of applications. The first is a budgeted approach, where each application has a specified instruction and data memory usage within the manifest file. The other is dynamic sizing, where the size of the memory segments are determined based on the actual memory used by the application.
Budget
In the budget approach the memory requirements of each application are specified within the manifest, and then the firmware-bundler allocates out portions of the memory hierarchy to match those requirements. This approach verifies that the application can fit in the specified memory region, and if not the compilation will fail with an appropriate error message. Since the memory regions are known a priori, the firmware-bundler can use a single pass compilation of each binary.
Pros:
- Requires a single build pass for every application, which can significantly reduce overall build time.
- Memory requirements are explicit, and are verified as part of the compilation process.
Cons:
- Manual adjustment is required if applications grow or change sizes in a way that requires different allocation of the overall memory space.
Dynamic Sizing
In the dynamic sizing approach the memory requirements of individual applications are not defined, but determined dynamically as part of the bundling process. While this doesn't effect the ROM binary (since it is the only binary deployed to ROM, it always gets the full memory hieararchy), this does have a significant impact on the runtime applications. In this model the exec and data memory must not be specified for runtime applications, as it will be dynamically sized by the bundler.
To determine the size of each binary a two pass build process is used. First all runtime applications are built with the entire memory space of the device dedicated to them. From this pass the size of each binary is determined. Once the size of every application is known, the bundler then does a second pass where each binary only receives the amount of memory it requires.
Pros:
- Allows dynamic memory allocation to applications, without manual intervention.
Cons:
- Requires 2 build passes per runtime application.
Manifest
The principle mechanism for configuring a run of the firmware-bundler is a manifest toml file describing the applications to build and the platform to deploy them to. By describing the memory layout of the bundle, explicit linker files can be generated and budgets can be checked prior executing the build itself, allowing a single pass architecture (See notes for support for 2 pass builds).
The following is a budget (one-pass) sample manifest:
# The platform describes where the firmware bundle will be deployed to. It
# should describe hardware characteristics like memory hierarchy and rustc
# target tuple.
[platform]
# The name of this platform. This is used for naming various binaries, but does
# not effect the contents of the output bundle.
name = "user-recognizable-name"
# The target tuple to compile this bundle for. If any rustc configuration
# options are required they should be set for this tuple in the `config.toml`
# file.
tuple = "riscv32imc-unknown-none-elf"
# The alignment each binary's intruction and data blocks should match. If not
# specified defaults to 8.
default_alignment = 4
# The page size for Tock linker scripts. If not defined the base page size of
# kernel layout linker script is used.
page_size = 256
# The following sections are used to describe the memory layout of the platform
# under build. The offset describes where in the space the section starts, and
# size indicates its length.
#
# Both are in bytes and can be specified in either decimal or hexadecimal (with
# 0x prefix).
# The ROM memory. This is where the ROM binary's instructions will reside.
[platform.rom]
offset = 0x0000_0000
size = 0x2_0000
# The ITCM (or ICCM) memory. This is where the runtime's instructions will
# reside, both kernel and user space applications.
[platform.itcm]
offset = 0x1000_0000
size = 0x4_0000
# The RAM memory. This is where the runtime's data will reside, both kernel
# and user space applications.
[platform.ram]
offset = 0x2000_0000
size = 0x4_0000
# An optional memory for ROM code's data.
[platform.dccm]
offset = 0x3000_0000
size = 0x4000
# An optional memory indicating the location within the memory hieararchy of
# flash.
[platform.flash]
offset = 0x3BFE_0000
size = 0x2_0000
# After the platform descriptions are the binaries to include in the bundled
# artifacts. The 'name' field in each binary should match a package within the
# Cargo workspace the firmware-bundler is being run in.
# The rom binary is an optional application run at power on. It allocates its
# instructions from the rom memory block and data from dccm.
[rom]
# The name of the rom binary. It must match a package within the Cargo
# workspace.
name = "rom"
# The instruction memory required for the binary.
exec_mem = {
# The amount of memory reserved for this application. It must fit within the
# given constraint.
"size" = 0x2000
# The alignment the offset of the binary must match. If not provided the
# default alignment of the platform is used.
"alignment" = 0x10
}
# The data memory required for this binary.
data_mem = {
"size" = 0x4000
}
# The amount of space within RAM to reserve for the stack. This, in addition
# to the exception_stack must be less than or equal to the RAM size.
stack = 0x2800
# The amount of space within RAM to reserve for the excption stack. This will
# used in exception handling to avoid any issues with the application stack,
# like stack overflows. This is an optional field.
exception_stack = 0x800
# The kernel tockOS binary. It must be specified and contains the same fields
# as the rom.
[kernel]
name = "kernel"
exec_mem = {
"size" = 0x2_0000
}
data_mem = {
"size" = 0x1_0000
}
stack = 0xa000
# After the kernel, an unlimited number of user applications can be specified.
# These will allocate from the ITCM and RAM in the order they are specified.
[[app]]
name = "user-app-1"
exec_mem = {
"size" = 0x2_0000
}
data_mem = {
"size" = 0x1_0000
}
stack = 0xa000
[[app]]
name = "user-app-2"
exec_mem = {
"size" = 0x1_0000
}
data_mem = {
"size" = 0x3_0000
}
stack = 0x2_0000
A dynamic sizing manifest would resemble the following:
# The platform retains the same configuration as above with the additional
# specification of dynamic sizing.
[platform]
name = "dynamic-sizing-example"
tuple = "riscv32imc-unknown-none-elf"
# This turns on dynamic sizing. If this is specified the sizes of the
# instruction and data memory for runtime applications will be calculated by
# the bundler.
dynamic_sizing = true
# .... rest of platform configuration as described above ....
# The application specifications, with the exec memory and data memory sections
# removed. The stack must be defined in this case.
[rom]
name = "rom"
stack = 0x2800
exception_stack = 0x800
[kernel]
name = "kernel"
stack = 0xa000
# After the kernel, an unlimited number of user applications can be specified.
# They will allocate in the order they are specified.
[[app]]
name = "user-app-1"
stack = 0xa000
# The amount of space to reserve in the data allocation in a user space
# application for TockOS grants. This setting is only applied during dynamic
# sizing, otherwise the `data_mem` budget should account for it. If not set
# this will default to 4Kb.
grant_space = 0x4000
[[app]]
name = "user-app-2"
stack = 0x2_0000
Integration
It is intended for mcu-firmware-bundler to be integrated as an xtask command. xtask is a paradigm for extending Cargo to support additional build commands as required by a project. Once you've integrated xtask, you should be able to add the mcu-firmware-bundler::Command as a separate command within your xtask match logic. This will allow a user to invoke the firmware bundler commands from the cli, and easily produce bundled binaries using the same technique used to build and test rust repositories normally.
E.g.
cargo xtask <firmware-bundler-command> bundle path/to/manifest_file.toml
Build outputs from the mcu-firmware-bundler are placed within the target/<target-tuple> directory for the Cargo workspace. Linker scripts are placed within the linker-scripts directory while binary outputs are placed in the release directory.
To get a full understanding of the firmware bundler commands and their various options, invoke the help command, which provides both the arguments and helpful documentation.
cargo xtask <firmware-bundler-command> --help
For an example of integration, take a look at the caliptra-mcu-sw integration of the firmware bundler in caliptra-mcu-sw/xtask/src/main.rs
a6784b6
Caliptra Subsystem Provisioning Guide
Goal / Disclaimer
This document aims to provide Caliptra Subsystem (SS) silicon vendors/manufacturers with guidance on how to provision devices during manufacturing. Nothing in this document is a requirement for Caliptra Trademark compliance. Note, this document focuses on the technical and operational aspects of provisioning a device with Caliptra Subsystem. Details on how to secure provisioning infrastructure to meet various certification requirements are outside the scope of this document.
Assets
There are two categories of assets that must be provisioned into a product that integrates Caliptra Subsystem before the device becomes fully operational:
- fuses
- IDevID (UDS) certificate
Fuses
Caliptra SS contains a One-Time Programmable (OTP) fuse memory bank that must be provisioned during SoC manufacturing. The fuse bank contains several logical fields organized across a set of partitions. Additionally, Caliptra SS contains a fuse-backed lifecycle FSM (known as the lifecycle controller), whose states gate debug capabilities of a device, thus requiring specific fuse fields be programmed in specific life-cycle states. The table below describes the fuse map and the lifecycle states they should be provisioned by.
Certificates
When requested, the IDevID CSR is generated by the Caliptra Core ROM after receiving the UDS Seed from the Caliptra Subsystem fuse controller. It should be endorsed by an external CA protected via an HSM on an isolated manufacturing network1.
There are two different IDevID certificates to be generated, based on the cryptographic keys they are constructed for:
- ECC secp384r1
- ML-DSA-87
The signature sizes for each are shown in the following table. These signatures should be stored in the larger SoCβs nonvolatile storage or other accessible service.
| Certificate type | Signature size (Bytes) |
|---|---|
| ECC Secp384r1 | 96 |
| ML-DSA-87 | 4627 |
To be able to reconstruct the IDevID certificate on boot, Caliptra also requires the original certificate attributes be stored in the SoC's fuses. The size and encoding of this data is fixed for both certificate types (ECC and ML-DSA), and detailed in the Caliptra specification.
Network isolation requirements are often dictated by certification requirements, while are outside the scope of this document.
Provisioning Sequence
The Caliptra Subsystem provisioning sequence is described below. The provisioning sequence is designed to suit various requirements among downstream integrators, including:
- providing an ATE-friendly provisioning sequence where DUT operations can be performed: a. directly over JTAG, and/or b. by loading custom MCU firmware into SRAM (via JTAG).
- The ability to program different fuse partitions at various lifecycle stages.
In addition to the provisioning flow description, the Caliptra project aims to provide a general reference provisioning flow, and supporting tooling / firmware, upstream. These are currently under development, and tied to a caliptra-mcu-sw GitHub milestone.
Before describing the sequence, we provide some background information on two important hardware blocks that facilitate the provisioning sequence, namely:
- lifecycle controller, and
- fuse controller.
Background: Lifecycle Controller Architecture
Caliptra Subsystem provides a hardware backed lifecycle FSM (known as the lifecycle controller) that transitions through several lifecycle states shown in the diagram below:
States only move forward and persist across chip resets; once advanced, it cannot revert to a previous state as lifecycle states are encoded in fuses. Transitions between some states require the use of password-like tokens. All tokens are provisioned in fuses, except the "Raw Unlock Token", which is a netlist constant. The purpose of each lifecycle state is described below. Additionally below is a table that summarizes which states allow debug (i.e. JTAG and DFT) access to various Caliptra Subsystem and SoC components.
| Lifecycle State | JTAG Access | DFT Access |
|---|---|---|
| Raw | LCC | None |
| TestUnlocked[0β7] | LCC, CLTAP*, MCU, Core | SS, SoC |
| TestLocked[0β7] | LCC | None |
| Manuf | LCC, CLTAP* | None |
| Manuf Debug Unlock | LCC, CLTAP*, MCU, Core | SoC* |
| Prod | LCC | None |
| Prod Debug Unlock | LCC, CLTAP*, MCU, Core | SoC* |
| ProdEnd | LCC | None |
| RMA | LCC, CLTAP*, MCU, Core | SS, SoC |
| Scrap | LCC | None |
* Integration Configurable
CLTAP = Chip Level TAP
TestUnlocked[0β7] states are used for initial testing to ensure chip health. All lifecycle tokens in the SECRET_LC_TRANSITION_PARTITION fuse partition should be provisioned in TestUnlocked0 to allow further actuation of the lifecycle controller during manufacturing. After testing, the chip is locked by entering a TestLocked[0β7] state to close debug access.
Using a provisioned token, a chip can transition from any of the TestUnlock[0β7] / TestLocked[0β7] states to the Manuf(acturing) state. In this state, debug access is once again opened, and the UDS and other manufacturing-specific fuses are injected. All production debug unlock tokens for the production state must be provisioned in this state, and the partition locked.
After manufacturing, the chip enters a production state (Prod or ProdEnd). Additional fuse provisioning (e.g., field entropy, revocation bits) may occur here, but only for fuse partitions not already locked in manufacturing. Entry into debug mode in production uses signature authentication with public keys, supporting multiple debug levels (up to 8), each with different privileges.
Production End & RMA marks the end of the chipβs functional lifecycle. The chip is no longer considered part of the active fleet and is not used for regular operations. Additionally, from any lifecycle state, the chip may transition (unconditionally without token) to the Scrap state, where fuse zeroization is performed, in accordance with FIPS requirements, to securely erase all secret assets stored in fuses.
Background: Fuse Controller
As mentioned above, Caliptra Subsystem contains a One-Time Programmable (OTP) fuse memory bank that is controlled by a hardware block known as the fuse_ctrl (or fuse controller). The OTP memory bank is split into partitions, and each partition:
- contains a digest field over the partition, and
- can be write-locked by writing to the digest field.
The locking mechanism prevents further changes to certain partitions (e.g., UDS). Once locked, these fuses cannot be overwritten for the lifetime of the device, with the exception of zeroizable partitions which may be cleared once a device is decommissioned. While the fuse memory map may be expanded for a particular integration, see the integration guide for more details, a baseline memory map is provided upstream.
Provisioning Sequence
Below is a summary of the provisioning flow required to bring a Caliptra Subsystem device from a raw lifecycle state to a production lifecycle state. This flow assumes all devices in a raw state have all fuses in their, unprogrammed, initial state, i.e., all bits set to zeros. Additionally, while this flow may be performed at a manufacturing stage of the integrator's choosing, we describe the following operations from the perspective of a tester, as if the operations were performed entirely at a Final Test (FT) silicon manufacturing stage with the aid of an Automated Test Equipment (ATE).
- Tester drives a lifecycle transition via JTAG: Raw β TestUnlocked0
- Perform wafer sort testing.
- Tester programs the following fuses (via JTAG or by loading MCU firmware loaded to SRAM):
- vendor test
- Lifecycle tokens
- Manuf debug unlock token
- Perform more wafer sort testing.
- Tester drives a lifecycle transition via JTAG: TestUnlocked* β Manuf
- Tester programs remaining fuses:
- Secure Boot Key hashes / types
- UDS fuses via programming sequence with Caliptra Core API
- Tester requests Caliptra Core to generate HMAC endorsed IDevID CSR and extract from DUT.
- Tester forward CSR and HMAC to HSM for authentication and endorsement.
- HSM validates HMAC and endorses IDevID TBS certificate.
- [Optional] Tester program IDevID certificate into integration-specific non-volatile storage.
- Tester drives a lifecycle transition via JTAG: Manuf β Prod[End]
Appendix
Lifecycle State Constraints for Provisioning Fuses
The fuse partition below corresponds to the Caliptra Subsystem 2.1.1 reference fuse map.
| Partition | Item | Size [B] | Lifecycle State |
|---|---|---|---|
| SW_TEST_UNLOCK_PARTITION | CPTRA_SS_MANUF_DEBUG_UNLOCK_TOKEN | 64 | TEST_UNLOCKED |
| SECRET_MANUF_PARTITION | CPTRA_CORE_UDS_SEED | 64 | MANUF |
| SECRET_PROD_PARTITION_0 | CPTRA_CORE_FIELD_ENTROPY_0 | 8 | In-Field |
| SECRET_PROD_PARTITION_1 | CPTRA_CORE_FIELD_ENTROPY_1 | 8 | In-Field |
| SECRET_PROD_PARTITION_2 | CPTRA_CORE_FIELD_ENTROPY_2 | 8 | In-Field |
| SECRET_PROD_PARTITION_3 | CPTRA_CORE_FIELD_ENTROPY_3 | 8 | In-Field |
| SW_MANUF_PARTITION | CPTRA_CORE_ANTI_ROLLBACK_DISABLE | 4 | MANUF |
| CPTRA_CORE_IDEVID_CERT_IDEVID_ATTR | 96 | MANUF | |
| SOC_SPECIFIC_IDEVID_CERTIFICATE | 4 | MANUF | |
| CPTRA_CORE_IDEVID_MANUF_HSM_IDENTIFIER | 16 | MANUF | |
| CPTRA_CORE_SOC_STEPPING_ID | 4 | MANUF | |
| CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_0 | 48 | MANUF | |
| CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_1 | 48 | MANUF | |
| CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_2 | 48 | MANUF | |
| CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_3 | 48 | MANUF | |
| CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_4 | 48 | MANUF | |
| CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_5 | 48 | MANUF | |
| CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_6 | 48 | MANUF | |
| CPTRA_SS_PROD_DEBUG_UNLOCK_PKS_7 | 48 | MANUF | |
| SECRET_LC_TRANSITION_PARTITION | CPTRA_SS_TEST_UNLOCK_TOKEN_1 | 16 | TEST_UNLOCKED |
| CPTRA_SS_TEST_UNLOCK_TOKEN_2 | 16 | TEST_UNLOCKED | |
| CPTRA_SS_TEST_UNLOCK_TOKEN_3 | 16 | TEST_UNLOCKED | |
| CPTRA_SS_TEST_UNLOCK_TOKEN_4 | 16 | TEST_UNLOCKED | |
| CPTRA_SS_TEST_UNLOCK_TOKEN_5 | 16 | TEST_UNLOCKED | |
| CPTRA_SS_TEST_UNLOCK_TOKEN_6 | 16 | TEST_UNLOCKED | |
| CPTRA_SS_TEST_UNLOCK_TOKEN_7 | 16 | TEST_UNLOCKED | |
| CPTRA_SS_TEST_EXIT_TO_MANUF_TOKEN | 16 | TEST_UNLOCKED | |
| CPTRA_SS_MANUF_TO_PROD_TOKEN | 16 | TEST_UNLOCKED | |
| CPTRA_SS_PROD_TO_PROD_END_TOKEN | 16 | TEST_UNLOCKED | |
| CPTRA_SS_RMA_TOKEN | 16 | TEST_UNLOCKED | |
| SVN_PARTITION | CPTRA_CORE_FMC_KEY_MANIFEST_SVN | 4 | In-Field |
| CPTRA_CORE_RUNTIME_SVN | 16 | In-Field | |
| CPTRA_CORE_SOC_MANIFEST_SVN | 16 | In-Field | |
| CPTRA_CORE_SOC_MANIFEST_MAX_SVN | 4 | In-Field | |
| VENDOR_TEST_PARTITION | VENDOR_TEST | 32 | TEST_UNLOCKED |
| VENDOR_HASHES_MANUF_PARTITION | CPTRA_CORE_VENDOR_PK_HASH_0 | 48 | MANUF |
| CPTRA_CORE_PQC_KEY_TYPE_0 | 4 | MANUF | |
| VENDOR_HASHES_PROD_PARTITION | CPTRA_SS_OWNER_PK_HASH | 48 | MANUF |
| CPTRA_SS_OWNER_PQC_KEY_TYPE | 4 | MANUF | |
| CPTRA_SS_OWNER_PK_HASH_VALID | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_1 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_1 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_2 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_2 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_3 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_3 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_4 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_4 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_5 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_5 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_6 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_6 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_7 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_7 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_8 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_8 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_9 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_9 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_10 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_10 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_11 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_11 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_12 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_12 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_13 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_13 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_14 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_14 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_15 | 48 | MANUF | |
| CPTRA_CORE_PQC_KEY_TYPE_15 | 4 | MANUF | |
| CPTRA_CORE_VENDOR_PK_HASH_VALID | 16 | MANUF | |
| VENDOR_REVOCATIONS_PROD_PARTITION | CPTRA_SS_OWNER_ECC_REVOCATION | 4 | In-Field |
| CPTRA_SS_OWNER_LMS_REVOCATION | 4 | In-Field | |
| CPTRA_SS_OWNER_MLDSA_REVOCATION | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_0 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_0 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_0 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_1 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_1 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_1 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_2 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_2 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_2 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_3 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_3 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_3 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_4 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_4 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_4 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_5 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_5 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_5 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_6 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_6 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_6 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_7 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_7 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_7 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_8 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_8 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_8 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_9 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_9 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_9 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_10 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_10 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_10 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_11 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_11 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_11 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_12 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_12 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_12 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_13 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_13 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_13 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_14 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_14 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_14 | 4 | In-Field | |
| CPTRA_CORE_ECC_REVOCATION_15 | 4 | In-Field | |
| CPTRA_CORE_LMS_REVOCATION_15 | 4 | In-Field | |
| CPTRA_CORE_MLDSA_REVOCATION_15 | 4 | In-Field | |
| VENDOR_SECRET_PROD_PARTITION | CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_0 | 32 | PROD |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_1 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_2 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_3 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_4 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_5 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_6 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_7 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_8 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_9 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_10 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_11 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_12 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_13 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_14 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_SECRET_FUSE_15 | 32 | PROD | |
| VENDOR_NON_SECRET_PROD_PARTITION | CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_0 | 32 | PROD |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_1 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_2 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_3 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_4 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_5 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_6 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_7 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_8 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_9 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_10 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_11 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_12 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_13 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_14 | 32 | PROD | |
| CPTRA_SS_VENDOR_SPECIFIC_NON_SECRET_FUSE_15 | 32 | PROD | |
| CPTRA_SS_LOCK_HEK_PROD_0 | CPTRA_SS_LOCK_HEK_PROD_0_RATCHET_SEED | 48 | PROD |
| CPTRA_SS_LOCK_HEK_PROD_1 | CPTRA_SS_LOCK_HEK_PROD_1_RATCHET_SEED | 48 | PROD |
| CPTRA_SS_LOCK_HEK_PROD_2 | CPTRA_SS_LOCK_HEK_PROD_2_RATCHET_SEED | 48 | PROD |
| CPTRA_SS_LOCK_HEK_PROD_3 | CPTRA_SS_LOCK_HEK_PROD_3_RATCHET_SEED | 48 | PROD |
| CPTRA_SS_LOCK_HEK_PROD_4 | CPTRA_SS_LOCK_HEK_PROD_4_RATCHET_SEED | 48 | PROD |
| CPTRA_SS_LOCK_HEK_PROD_5 | CPTRA_SS_LOCK_HEK_PROD_5_RATCHET_SEED | 48 | PROD |
| CPTRA_SS_LOCK_HEK_PROD_6 | CPTRA_SS_LOCK_HEK_PROD_6_RATCHET_SEED | 48 | PROD |
| CPTRA_SS_LOCK_HEK_PROD_7 | CPTRA_SS_LOCK_HEK_PROD_7_RATCHET_SEED | 48 | PROD |
Glossary
The following acronyms and abbreviations are used throughout this document.
| ATE | Automated Test Equipment |
|---|---|
| ODM | Original Design Manufacturer |
| OSAT | Outsourced Semiconductor Assembly and Test |
| FT | Final Test |
| SLT | System Level Test |
| UDS | Unique Device Secret |
| CSR | Certificate Signing Request |
a6784b6
Working with an FPGA
This section covers how to perform common MCU flows on an FPGA. You can always fallback to working on the FPGA directly, as it is an ARM Linux host, albeit low powered.
Pre Requisites
Host System
The machine that is used for development and cross compilation should have:
- Rust
- Podman
- Alternatively you can use docker, but it will need to support rootless commands. Here are the steps here.
- rsync
- git
- Cargo-nextest
$ cargo +1.93 install cargo-nextest --locked
FPGA System
The FPGA should have the following installed (If you are not using a Caliptra image):
- rsync
- git
- make
- gcc
- cargo-nextest
cargo-nextestshould be useable by the root user. Consider installing it to the system path/usr/bin.
Suggestion: Download the latest FPGA Image from the Caliptra-SW FPGA Image build job. This ensures you are testing with the same system used in the FPGA CI. Developer images have a "-dev" suffix.
Suggested Development Flow
Prefer to develop on your main machine and use xtask to test your changes on the FPGA via ssh.
Setup SSH config
xtask will access the FPGA over SSH. You will need an SSH config to define how to do this.
This config should be added to ~/.ssh/config.
Host <FPGA-NAME> # Update me!
Hostname <FPGA-IP-ADDRESS> # Update me!
User ubuntu # Use "root" on CI image.
Developing over SSH
Most xtask-fpga fpga commands support running over SSH. This is the preferred and tested path. The SSH target is passed with the --target-host flag.
The xtask tool will cross-compile and then copy binaries to the FPGA over the SSH connection.
Bitstream
Caliptra Images
The Caliptra images use a segmented bitstream. The bitstream is automatically loaded by the bootstrap command, based on the FPGA configuration type.
NOTE: The core & subsystem base images are incompatible, and you must be sure to use the correct base image for the configuration you are using.
For example:
- core-on-subsystem => Subsystem base image
- subsystem => Subsystem base image
- core => Core base image
Bitstream source
The subsystem bitstream is pulled from the subsystem.toml. The core bitstream is pulled from "core.toml" from the active caliptra-sw folder.
Ubuntu Image
Follow the instructions in hw/fpga/README.md on building and loading a bitstream.
FPGA bootstrap
The FPGA needs to be bootstrapped each time it is booted. This ensures that the kernel modules we use in this repo are present.
Run cargo xtask-fpga fpga bootstrap --target-host $SSH-FPGA-NAME to bootstrap the FPGA.
Configurations
The xtask flows supports three different configurations:
- "Subsystem": This mode configures the FPGA to run Caliptra MCU flows.
- "Core-on-Subsystem": This mode configures the FPGA to run Caliptra-SW flows for a subsystem Caliptra.
- "Core": This mode allows running Caliptra in Core mode on the FPGA.
- Note: You will need to override caliptra-sw to point to a local directory in the workspace Cargo.toml.
For example:
$ cargo xtask-fpga fpga bootstrap # The default configration is subsystem
$ cargo xtask-fpga fpga bootstrap --configuration subsystem
$ cargo xtask-fpga fpga bootstrap --configuration core-on-subsystem
$ cargo xtask-fpga fpga bootstrap --configuration core
Cross compiling
Firmware
Run cargo xtask-fpga fpga build --target-host $SSH-FPGA-NAME to create a firmware bundle. Using the --target-host flag will automatically copy the firmware to the FPGA host.
This command should be re-run after making any firmware changes.
Filter Caliptra Core firmeware
To improve build time when using the core-on-subsystem FPGA configuration, you can use the fw-id parameter to filter out firmware that you are uninterested in.
For example, the following only builds the Caliptra Core ROM
$ cargo xtask-fpga build --fw-id caliptra-rom
Only build MCU firmware
For configurations that additionally build Caliptra Core firmware, you can disable this with the --mcu flag.
For example:
$ cargo xtask-fpga build --mcu
Test Binaries
Run cargo xtask-fpga fpga build-test --target-host $SSH-FPGA-NAME to create a test archive. Using the --target-host flag will automatically copy the test binaries to the FPGA host.
This command should be re-run after making any test changes.
Test Workflow
FPGA Bootstrapping and FW/Test Building
A developer verifying changes against an FPGA should use the following sequence of commands to bootstrap their FPGA board by:
- downloading the caliptra-mcu-sw repo on their board,
- building all Caliptra Core and MCU firmware collateral, and
- building all FPGA tests for the FPGA environment.
\\( cargo xtask-fpga fpga bootstrap --target-host \\)SSH-FPGA-NAME # Run this only once per boot.
\\( cargo xtask-fpga fpga build --target-host \\)SSH-FPGA-NAME # Build firmware. Re-run every time firmware changes.
\\( cargo xtask-fpga fpga build-test --target-host \\)SSH-FPGA-NAME # Build test binaries. Re-run every time tests change.
Dispatching all FPGA Tests
A developer verifying changes against an FPGA can dispatch the entire FPGA test in a single shot by running:
\\( cargo xtask-fpga fpga test --target-host \\)SSH-FPGA-NAME
Dispatching a Single FPGA Test
A developer that only wishes to run a single test on the FPGA can replace the last command with the following:
# For example:
\\( cargo xtask-fpga fpga test --target-host \\)SSH-FPGA-NAME \
--test-filter="package(mcu-hw-model) and test(test_hash_token)"
The test filter flag is a wrapper for a Cargo Nextest filter set.
Test Caliptra Core on FPGA
The xtask workflow also supports running caliptra-sw tests on a subsystem FPGA.
Bootstrap and Build Test Binaries / FW
\\( cargo xtask-fpga fpga bootstrap --target-host \\)SSH-FPGA-NAME --configuration core-on-subsystem # Run this only once per boot. Re-run bootstrap to change configurations
\\( cargo xtask-fpga fpga build --target-host \\)SSH-FPGA-NAME # Build firmware. Re-run every time firmware changes.
\\( cargo xtask-fpga fpga build-test --target-host \\)SSH-FPGA-NAME # Build test binaries. Re-run every time tests change.
Running on FPGA
NOTE: The run command does not yet support running over ssh.
Firmware files
You can build firmware binaries (ROM, runtime, manifest, etc.) using a combination of:
cargo xtask-fpga rom-build --platform fpgacargo xtask-fpga runtime-build --platform fpgacargo xtask-fpga all-build --platform fpga
The all-build command will build Caliptra ROM, Caliptra firmware bundle, MCU ROM, MCU runtime, and the SoC manifest and package them all together in a ZIP file, which the fpga-run xtask can run.
These commands can be run on any host. It is not recommended to run them on the FPGA host as it is very slow (the build can take over an hour the first time); instead it is better to run the command on a different host and use scp or rsync to copy the ZIP in.
Running ROM and firmware
cargo xtask-fpga fpga-run or ./xtask-bin fpga-run can be used to run ROMs and firmware, either directly or from a ZIP file, e.g.,
./xtask-bin fpga-run --zip all-fw.zip
It also supports additional command-line options for testing various flows.
UDS Provisioning
Preliminaries
- Build OpenOCD 0.12.0 with
--enable-sysfsgpio. - Install gdb-multiarch
- Run a ROM flow with a blank OTP memory to transition the lifecycle state to
TestUnlocked0. - Run a ROM to burn lifecycle tokens for the other transitions.
- Run a ROM flow to transition the lifecycle state to
Dev(this maps to the Manufacturing device lifecycle for Caliptra). - Start a run with the bootfsm_break set to 1.
- Start the OpenOCD server:
sudo openocd --file openocd_caliptra.txt
- Connect to OpenOCD
telnet localhost 4444
- Verify connectivity in telnet:
> riscv.cpu riscv dmi_read 0x74
0xcccccccc
- Write the request
> riscv.cpu riscv dmi_write 0x70 4
> riscv.cpu riscv dmi_write 0x61 1
OTP
There is an example fully provisioned (UDS and FE burned and transitioned into
production) .vmem file for loading into the OTP via the --otp in the
repository in
otp-prod-fe.vmem.
Troubleshooting
Error: fatal: unable to access 'https://github.com/chipsalliance/caliptra-mcu-sw/': server certificate verification failed. CAfile: none CRLfile: none
This error while bootstrapping could be due to the time on the the FPGA being incorrect. The clock can be resynchronized by running
$ sudo timedatectl set-ntp true
a6784b6
Caliptra Utility Host Library Design
Overview
The Caliptra Utility host library is a modular, extensible, and transport-agnostic Rust library designed to enable the development of utility tools that can run on both BMC and host systems. These utilities facilitate both in-band and out-of-band communication with the Caliptra subsystem on the device, supporting a variety of transport interfaces such as MCU mailbox and MCTP VDM. The library provides a unified API for all supported Caliptra commands, along with C bindings for integration with C/C++ applications, making it easy to build robust management, provisioning, and diagnostic tools for diverse deployment environments.
Design Goals
- Modularity: Clear separation between transport layer, command processing, and application logic
- Extensibility: Easy addition of new commands and transport mechanisms
- Transport Agnostic: Support multiple communication channels without changing application code
- Type Safety: Strong typing for command structures and responses
- Thread Safety: Safe for use in multi-threaded environments
Use Cases
- Device Identification and Capabilities
- Retrieve firmware versions, unique device identifiers, and device capabilities to ensure compatibility and proper configuration.
- Query device-specific information such as chip identifiers or subsystem details.
- Debugging and Diagnostics
- Retrieve debug logs to analyze device behavior, diagnose issues, and monitor runtime states.
- Clear logs to reset diagnostic data and maintain storage efficiency.
- Certificate Management
- Export Certificate Signing Requests (CSRs) for device keys to facilitate secure provisioning.
- Import signed certificates to establish a trusted certificate chain for device authentication.
- Debug Unlock Mechanisms
- Facilitate secure debugging in production environments
- Ensure controlled access to debugging features
- Cryptographic Services
- AES encryption and decryption
- SHA hashing
- Random number generation
- Digital signing
- Signature verification
- Key exchange
Architecture
Layered framework
flowchart TD
classDef wideBox font-size:12px, padding:10px;
A["Utility Application"]:::wideBox
subgraph Library["Caliptra Utility Host Library"]
B["Command Layer<br>(High-level APIs, transport-agnostic)"]:::wideBox
C["Core Layer<br>(Command builder, Response parser, <br/>protocol mapping, session mgmt)"]:::wideBox
D["Transport Abstraction Layer<br>(Unified interface for all transports)"]:::wideBox
E["Transport Implementation Layer<br>(SoC Mailbox, MCTP VDM, <br/>Custom transports etc.)"]:::wideBox
end
A --> B
B --> C
C --> D
D --> E
Component Descriptions
Command Layer
- Purpose: Provides high-level, transport-agnostic APIs for Caliptra commands
- Functionality:
- Exposes simple function calls for each Caliptra command (e.g.,
caliptra_get_device_id()) - Manages stateful operations like multi-part SHA/AES computations
- Maintains command contexts across multiple related operations
- Handles command-specific validation and parameter checking
- Abstracts complex command sequences into simple APIs
- Exposes simple function calls for each Caliptra command (e.g.,
- Data Flow: Receives API calls β Validates parameters β Passes to Core Layer for execution
Core Layer
- Purpose: Handles the mechanics of command execution and protocol management
- Functionality:
- Builds properly formatted command packets with headers and checksums
- Parses and validates responses from the device
- Implements retry logic for transient failures
- Manages command sequencing and state machines
- Handles protocol-level error recovery
- Data Flow: Receives command requests β Formats packets β Uses Transport Layer β Parses responses
Transport Abstraction Layer
- Purpose: Provides a unified interface to different physical transport mechanisms
- Functionality:
- Defines standard operations (open, close, send, receive)
- Manages connection lifecycle and readiness checks
- Handles transport-level timeouts and flow control
- Provides both synchronous and asynchronous operation modes
- Maps transport-specific errors to common error codes
- Data Flow: Receives formatted packets β Routes to appropriate transport β Returns raw responses
Transport Implementation Layer
- Purpose: Implements actual communication with hardware interfaces
- Functionality:
- SoC Mailbox: Direct memory-mapped I/O operations to mailbox registers
- MCTP VDM: Socket-based communication over MCTP protocol
- Future transports:
- Handles hardware-specific initialization and configuration
- Manages low-level data transfer and synchronization
- Data Flow: Receives bytes to send β Writes to hardware β Reads response bytes
Interface Design
The library is organized into several Rust crates, each providing specific functionality:
| Crate | Description |
|---|---|
caliptra-util-host-transport | Transport trait and implementations (Mailbox, etc.) |
caliptra-util-host-session | Session management and command execution |
caliptra-util-host-command-types | Command/response type definitions with zerocopy support |
caliptra-util-host-commands | High-level command API functions |
caliptra-util-host-osal | OS abstraction layer for portability |
caliptra-util-host-cbinding | C bindings for the library |
Transport Layer
The transport layer provides an abstraction for device communication through the Transport trait:
#![allow(unused)] fn main() { /// Transport trait for device communication pub trait Transport: Send + Sync { /// Connect to the device fn connect(&mut self) -> TransportResult<()>; /// Disconnect from the device fn disconnect(&mut self) -> TransportResult<()>; /// Send a command with the given command ID and payload fn send(&mut self, command_id: u32, data: &[u8]) -> TransportResult<()>; /// Receive response data into the provided buffer fn receive(&mut self, buffer: &mut [u8]) -> TransportResult<usize>; /// Check if the transport is connected fn is_connected(&self) -> bool; } /// Transport error types pub enum TransportError { ConnectionFailed(Option<&'static str>), SendFailed(Option<&'static str>), ReceiveFailed(Option<&'static str>), Timeout, NotSupported(&'static str), InvalidMessage, Disconnected, ConfigurationError(&'static str), IoError(&'static str), BufferError(&'static str), // ... additional error variants } /// Transport configuration pub struct TransportConfig { pub max_message_size: usize, pub timeout_ms: u32, } }
Mailbox Transport Implementation
The Mailbox transport provides communication through a mailbox driver:
#![allow(unused)] fn main() { /// Trait for hardware mailbox communication pub trait MailboxDriver: Send + Sync { /// Send a command and return response data fn send_command(&mut self, external_cmd: u32, payload: &[u8]) -> Result<&[u8], MailboxError>; /// Check if mailbox is ready fn is_ready(&self) -> bool; /// Connect to mailbox fn connect(&mut self) -> Result<(), MailboxError>; /// Disconnect from mailbox fn disconnect(&mut self) -> Result<(), MailboxError>; } /// Mailbox error types pub enum MailboxError { NotReady, Timeout, InvalidCommand, CommunicationError, BufferOverflow, DeviceError(u32), } /// Mailbox Transport using dynamic dispatch pub struct Mailbox<'a> { mailbox: &'a mut dyn MailboxDriver, // internal state... } impl<'a> Mailbox<'a> { /// Create a new mailbox transport with the given driver pub fn new(mailbox: &'a mut dyn MailboxDriver) -> Self; } impl Transport for Mailbox<'_> { // Transport trait implementation... } }
Session Layer
The session layer manages device connections and command execution:
#![allow(unused)] fn main() { /// Session state enumeration #[repr(u32)] pub enum SessionState { Disconnected = 0, Connecting = 1, Connected = 2, Authenticated = 3, Error = 4, } /// Session configuration pub struct SessionConfig { pub connection_timeout_ms: u32, pub command_timeout_ms: u32, pub max_retries: u8, pub keepalive_interval_ms: u32, pub auto_reconnect: bool, } /// Session statistics pub struct SessionStatistics { pub commands_sent: u64, pub commands_succeeded: u64, pub commands_failed: u64, pub bytes_sent: u64, pub bytes_received: u64, pub reconnect_count: u32, pub last_error_count: u32, } /// Session error enumeration pub enum SessionError { SessionNotFound(u32), InvalidState { current: SessionState, expected: SessionState }, TransportError(&'static str), OsalError(&'static str), ConfigurationError(&'static str), AuthenticationError(&'static str), ResourceError(&'static str), SerializationError(&'static str), MaxRetriesExceeded, InternalError(&'static str), Custom(&'static str), } /// Device session context using dynamic dispatch pub struct CaliptraSession<'t> { pub session_id: u32, pub state: SessionState, pub config: SessionConfig, pub stats: SessionStatistics, // internal fields... } impl<'t> CaliptraSession<'t> { /// Create a new session with the given transport pub fn new(session_id: u32, transport: &'t mut dyn Transport) -> SessionResult<Self>; /// Create session with custom configuration pub fn with_config( session_id: u32, transport: &'t mut dyn Transport, config: SessionConfig, ) -> SessionResult<Self>; /// Connect to the device pub fn connect(&mut self) -> SessionResult<()>; /// Disconnect from device pub fn disconnect(&mut self) -> SessionResult<()>; /// Check if session is connected and ready pub fn is_ready(&self) -> bool; /// Execute a structured command through the session pub fn execute_command<Req, Resp>(&mut self, command: &Req) -> SessionResult<Resp> where Req: CommandRequest, Resp: CommandResponse; /// Execute a command with explicit command ID pub fn execute_command_with_id<Req>( &mut self, command_id: CaliptraCommandId, request: &Req, ) -> SessionResult<Req::Response> where Req: CommandRequest + IntoBytes, Req::Response: FromBytes + Immutable; /// Get session info pub fn get_info(&self) -> SessionInfo; /// Handle session errors and recovery pub fn handle_error(&mut self, error: SessionError) -> SessionResult<()>; } }
Command Types Layer
Command and response structures are defined using zerocopy for efficient serialization:
#![allow(unused)] fn main() { /// Caliptra command IDs #[repr(u32)] pub enum CaliptraCommandId { // Device Info Commands (0x0001-0x000F) GetFirmwareVersion = 0x0001, GetDeviceCapabilities = 0x0002, GetDeviceId = 0x0003, GetDeviceInfo = 0x0004, // Certificate Commands (0x1001-0x101F) GetIdevidCert = 0x1001, GetLdevidCert = 0x1002, GetFmcAliasCert = 0x1003, GetRtAliasCert = 0x1004, GetCertChain = 0x1010, StoreCertificate = 0x1011, // Hash Commands (0x2001-0x2003) HashInit = 0x2001, HashUpdate = 0x2002, HashFinalize = 0x2003, // HMAC and Key Commands (0x2013-0x2016) Hmac = 0x2013, HmacKdfCounter = 0x2014, Import = 0x2015, Delete = 0x2016, // Symmetric Crypto Commands (0x3001-0x3015) AesEncryptInit = 0x3001, AesEncryptUpdate = 0x3002, AesDecryptInit = 0x3003, AesDecryptUpdate = 0x3004, AesGcmEncryptInit = 0x3010, AesGcmEncryptUpdate = 0x3011, AesGcmEncryptFinal = 0x3012, AesGcmDecryptInit = 0x3013, AesGcmDecryptUpdate = 0x3014, AesGcmDecryptFinal = 0x3015, // Asymmetric Crypto Commands (0x4001-0x402F) EcdsaSign = 0x4001, EcdsaVerify = 0x4002, EcdhDerive = 0x4003, // Debug Commands (0x7001-0x701F) DebugEcho = 0x7001, DebugGetStatus = 0x7002, DebugGetLog = 0x7005, // Fuse Commands (0x8001-0x801F) FuseRead = 0x8001, FuseWrite = 0x8002, FuseLock = 0x8003, } /// Command processing errors pub enum CommandError { InvalidCommand, InvalidRequest, InvalidResponse, InvalidResponseLength, ResponseTooLong, SerializationError, DeserializationError, ChecksumMismatch, Unsupported, BufferTooSmall, Custom(&'static str), } }
Example Command Types
#![allow(unused)] fn main() { /// Get device ID request #[repr(C)] #[derive(Debug, Clone, IntoBytes, FromBytes, Immutable)] pub struct GetDeviceIdRequest { // Empty request - no parameters needed } /// Get device ID response #[repr(C)] #[derive(Debug, Clone, IntoBytes, FromBytes, Immutable)] pub struct GetDeviceIdResponse { pub vendor_id: u16, pub device_id: u16, pub subsystem_vendor_id: u16, pub subsystem_id: u16, } impl CommandRequest for GetDeviceIdRequest { type Response = GetDeviceIdResponse; const COMMAND_ID: CaliptraCommandId = CaliptraCommandId::GetDeviceId; } /// Get firmware version request #[repr(C)] #[derive(Debug, Clone, IntoBytes, FromBytes, Immutable)] pub struct GetFirmwareVersionRequest { pub index: u32, // 0 = ROM, 1 = Runtime } /// Firmware version response #[repr(C)] #[derive(Debug, Clone, IntoBytes, FromBytes, Immutable)] pub struct GetFirmwareVersionResponse { pub common: CommonResponse, pub version: [u32; 4], // Major, minor, patch, build ... } }
Command API Layer
The command API provides high-level functions for interacting with Caliptra devices:
#![allow(unused)] fn main() { /// High-level result type for API functions pub type CaliptraResult<T> = Result<T, CaliptraApiError>; /// API-specific error types pub enum CaliptraApiError { Osal(OsalError), InvalidParameter(&'static str), SessionNotInitialized, TransportNotAvailable, CommandFailed(&'static str), SessionError(&'static str), } // ============================================================================ // Device Information Commands // ============================================================================ /// Get device identification information pub fn caliptra_cmd_get_device_id( session: &mut CaliptraSession, ) -> CaliptraResult<GetDeviceIdResponse>; /// Get device information pub fn caliptra_cmd_get_device_info( session: &mut CaliptraSession, info_type: u32, ) -> CaliptraResult<GetDeviceInfoResponse>; /// Get device capabilities pub fn caliptra_cmd_get_device_capabilities( session: &mut CaliptraSession, ) -> CaliptraResult<GetDeviceCapabilitiesResponse>; /// Get firmware version pub fn caliptra_cmd_get_firmware_version( session: &mut CaliptraSession, index: u32, // 0 = ROM, 1 = Runtime ) -> CaliptraResult<GetFirmwareVersionResponse>; }
C Bindings
The library provides C bindings through the cbinding crate for integration with C/C++ applications:
// Error code type for C API
typedef enum {
CALIPTRA_SUCCESS = 0,
CALIPTRA_ERROR_UNKNOWN = 1,
CALIPTRA_ERROR_INVALID_ARGUMENT = 2,
CALIPTRA_ERROR_TIMEOUT = 3,
CALIPTRA_ERROR_NOT_SUPPORTED = 4,
CALIPTRA_ERROR_TRANSPORT = 5,
CALIPTRA_ERROR_PROTOCOL = 6,
CALIPTRA_ERROR_DEVICE = 7,
CALIPTRA_ERROR_MEMORY = 8,
CALIPTRA_ERROR_BUSY = 9,
CALIPTRA_ERROR_STATE = 10,
CALIPTRA_ERROR_IO = 11,
} CaliptraError;
// Protocol types
typedef enum {
CALIPTRA_PROTOCOL_MAILBOX = 0,
CALIPTRA_PROTOCOL_MCTP_VDM = 1,
CALIPTRA_PROTOCOL_CUSTOM = 2,
} CaliptraProtocolType;
// Opaque transport handle
typedef struct CaliptraTransport CaliptraTransport;
// Session management
CaliptraError caliptra_session_create_with_protocol(
CaliptraTransport* transport,
CaliptraProtocolType protocol_type,
CaliptraSession** session
);
CaliptraError caliptra_session_connect(CaliptraSession* session);
CaliptraError caliptra_session_disconnect(CaliptraSession* session);
CaliptraError caliptra_session_destroy(CaliptraSession* session);
// Transport management
CaliptraError caliptra_transport_destroy(CaliptraTransport* transport);
// Device ID structure (C-compatible)
typedef struct {
uint16_t vendor_id;
uint16_t device_id;
uint16_t subsystem_vendor_id;
uint16_t subsystem_id;
} CaliptraDeviceId;
// Command APIs
CaliptraError caliptra_cmd_get_device_id(
CaliptraSession* session,
CaliptraDeviceId* device_id
);
Usage Example (Rust)
use caliptra_util_host_transport::{Mailbox, MailboxDriver}; use caliptra_util_host_session::CaliptraSession; use caliptra_util_host_commands::api::caliptra_cmd_get_device_id; // Implement MailboxDriver for your hardware struct MyMailboxDriver { /* ... */ } impl MailboxDriver for MyMailboxDriver { fn send_command(&mut self, cmd: u32, payload: &[u8]) -> Result<&[u8], MailboxError> { // Hardware-specific implementation } fn is_ready(&self) -> bool { true } fn connect(&mut self) -> Result<(), MailboxError> { Ok(()) } fn disconnect(&mut self) -> Result<(), MailboxError> { Ok(()) } } fn main() -> Result<(), Box<dyn std::error::Error>> { // Create mailbox driver let mut driver = MyMailboxDriver::new(); // Create transport let mut transport = Mailbox::new(&mut driver); // Create session let mut session = CaliptraSession::new(1, &mut transport)?; // Connect session.connect()?; // Get device ID let device_id = caliptra_cmd_get_device_id(&mut session)?; println!("Vendor ID: 0x{:04x}", device_id.vendor_id); println!("Device ID: 0x{:04x}", device_id.device_id); // Disconnect session.disconnect()?; Ok(()) }
Extensibility with Plugin Architecture
The Caliptra Utility host library is designed to be highly extensible, supporting the dynamic addition of new commands, protocols, and transport mechanisms at runtime through a plugin architecture. This approach enables third-party developers and system integrators to extend the libraryβs capabilities without modifying or recompiling the core library.
flowchart TD
subgraph App["Utility Application"]
A1[Main App/CLI]
A2["Plugin (Shared Library)"]
end
subgraph Library["Caliptra Utility Host Library"]
B1[Command Layer]
B2[Core Layer]
B3[Transport Abstraction Layer]
B4[Transport Impl Layer]
B5[Plugin Loader & Registry]
end
A1 -->|Loads at runtime| A2
A2 -- Registers new commands/transports/protocols --> B5
A1 -->|Uses APIs| B1
B1 --> B2
B2 --> B3
B3 --> B4
B5 -- Makes available to --> B1
- Plugins are shared libraries (e.g.,
.soor.dllfiles) that implement new commands, protocol handlers, or transport mechanisms. - The core library provides APIs to dynamically load, register, and discover these plugins at runtime.
- This design preserves the existing layered architecture, while enabling runtime extensibility and modularity.
How Plugins Work
-
Plugin Interface
Each plugin implements a standard interface and exports a descriptor structure.
#![allow(unused)] fn main() { /// Plugin descriptor containing metadata and registration function pub struct PluginDescriptor { /// Plugin name identifier pub name: &'static str, /// Plugin version string pub version: &'static str, /// Registration function called by the core library pub register: fn() -> CaliptraResult<()>, /// Optional cleanup function called on unload pub unregister: Option<fn() -> CaliptraResult<()>>, } /// Macro to export a plugin descriptor from a shared library #[macro_export] macro_rules! export_plugin { ($descriptor:expr) => { #[no_mangle] pub static CALIPTRA_PLUGIN_DESCRIPTOR: PluginDescriptor = $descriptor; }; } }The plugin's
registerfunction is called by the core library to register new commands, transports, or protocol handlers. -
Dynamic Loading
The core library provides APIs to load and unload plugins at runtime.
#![allow(unused)] fn main() { /// Plugin manager for loading and managing plugins pub struct PluginManager { // Internal state... } impl PluginManager { /// Create a new plugin manager pub fn new() -> Self; /// Load a plugin from the specified path pub fn load(&mut self, path: &Path) -> CaliptraResult<PluginHandle>; /// Unload a plugin by name pub fn unload(&mut self, name: &str) -> CaliptraResult<()>; /// Get a list of loaded plugins pub fn loaded_plugins(&self) -> &[PluginInfo]; } /// Handle to a loaded plugin pub struct PluginHandle { pub name: String, pub version: String, } /// Information about a loaded plugin pub struct PluginInfo { pub name: String, pub version: String, pub path: PathBuf, } }These use platform mechanisms (
dlopen/dlsymon Linux,LoadLibraryon Windows) to load plugins at runtime. -
Dynamic Registration
Plugins use registration and unregistration APIs to add or remove features.
#![allow(unused)] fn main() { /// Command descriptor for plugin-provided commands pub struct CommandDescriptor { /// Command name pub name: &'static str, /// Command ID pub command_id: u32, /// Command execution function pub execute: fn(&mut CaliptraSession, &[u8]) -> CaliptraResult<Vec<u8>>, /// Human-readable description pub description: &'static str, } /// Transport factory for plugin-provided transports pub trait TransportFactory: Send + Sync { /// Transport type name fn name(&self) -> &str; /// Create a new transport instance fn create(&self, config: &TransportConfig) -> CaliptraResult<Box<dyn Transport>>; /// Human-readable description fn description(&self) -> &str; } /// Protocol handler for plugin-provided protocol implementations pub trait ProtocolHandler: Send + Sync { /// Protocol type this handler supports fn protocol_type(&self) -> ProtocolType; /// Process an incoming message fn handle_message(&mut self, message: &[u8]) -> CaliptraResult<Vec<u8>>; } // Registration functions /// Register a new command pub fn register_command(desc: CommandDescriptor) -> CaliptraResult<()>; /// Unregister a command by name pub fn unregister_command(name: &str) -> CaliptraResult<()>; /// Register a new transport factory pub fn register_transport(factory: Box<dyn TransportFactory>) -> CaliptraResult<()>; /// Unregister a transport by name pub fn unregister_transport(name: &str) -> CaliptraResult<()>; /// Register a protocol handler pub fn register_protocol_handler(handler: Box<dyn ProtocolHandler>) -> CaliptraResult<()>; /// Unregister a protocol handler pub fn unregister_protocol_handler(protocol_type: ProtocolType) -> CaliptraResult<()>; }The library maintains internal registries of available commands, transports, and protocol handlers. When a plugin is unloaded, it should call the appropriate unregister functions to cleanly remove its features and release resources.
-
Discovery and Usage
Applications can enumerate available commands and transports at runtime.
#![allow(unused)] fn main() { /// Get a list of registered commands pub fn list_commands() -> Vec<&'static CommandDescriptor>; /// Get a list of registered transport factories pub fn list_transports() -> Vec<&'static str>; /// Get a command descriptor by name pub fn get_command(name: &str) -> Option<&'static CommandDescriptor>; /// Create a transport instance by name pub fn create_transport(name: &str, config: &TransportConfig) -> CaliptraResult<Box<dyn Transport>>; }This enables dynamic feature discovery and flexible application logic.
Extensibility for SPDM and PLDM Support
The Caliptra Utility host library architecture is designed to accommodate future extensions for industry-standard security and firmware management protocols. Specifically, the library can be extended to support SPDM (Security Protocol and Data Model) for device attestation and PLDM (Platform Level Data Model) for firmware updates by leveraging DMTF open source libraries.
Design Approach: Transport Adapter Pattern
Rather than implementing SPDM and PLDM protocols directly, the library adopts a transport adapter pattern that aligns with how DMTF libraries are designed:
- libspdm expects the application to provide transport send/receive callbacks
- libpldm is primarily a message encoding/decoding library that needs a transport mechanism
The host library's core value is providing transport abstraction. By providing transport adapters, the library enables applications to use libspdm and libpldm directly while leveraging the library's transport infrastructure.
Integration Architecture
flowchart TB
subgraph Host["Host"]
direction TB
subgraph App["Application"]
CAL_APP["Caliptra Commands"]
SPDM_APP["SPDM Attestation"]
PLDM_APP["PLDM Firmware Update"]
end
subgraph DMTF["Open Source Libraries"]
LSPDM["DMTF/libspdm"]
LPLDM["libpldm"]
end
subgraph HostLib["Caliptra Utility Host Library"]
direction TB
CMD["Command Layer"]
CORE["Core Layer"]
ADAPTER["Protocol Transport Adapters"]
TAL["Transport Abstraction Layer"]
end
end
CAL_APP --> CMD
SPDM_APP --> LSPDM
PLDM_APP --> LPLDM
LSPDM -->|"transport callbacks"| ADAPTER
LPLDM -->|"send/receive"| ADAPTER
CMD --> CORE
CORE --> TAL
ADAPTER --> TAL
OpenBMC Integration
For out-of-band management scenarios, the host library is eligible to integrate with OpenBMC:
- libpldm: OpenBMC already includes libpldm as a core component, enabling seamless integration for PLDM firmware update workflows
- DMTF/libspdm: Can be integrated into OpenBMC for SPDM attestation services
- D-Bus Services: The library's C bindings enable integration with OpenBMC D-Bus services for system management
- MCTP Transport: Leverages OpenBMC's MCTP infrastructure for device communication
Future Work
Note: The SPDM and PLDM transport adapter implementations are not provided in the current release. The architecture described above outlines the extensibility path for future integration. Third-party developers and system integrators can implement these adapters using the documented transport interfaces and DMTF open source libraries.
a6784b6
Network Recovery Boot
This document outlines the design for a lightweight network recovery boot mechanism for the Caliptra subsystem. The system enables the Caliptra SS to download firmware images over the network through a dedicated Network Boot Coprocessor within a ROM environment, providing a resilient fallback path when flash memory is corrupted.
The network boot coprocessor acts as an intermediary between remote image servers and the Caliptra SS, handling network communications including DHCP configuration, TFTP server discovery, and firmware image downloads. The system supports downloading multiple firmware components that includes the Caliptra SS and SoC images through a firmware ID-based mapping system.
Motivation
- Flash Dependency Risk: Boot failure if both flashes are corrupted
- Recovery Challenge: Physical intervention is costly in hyperscale environments
- Design Goals:
- Minimal MCU ROM footprint
- Consistent with OCP streaming boot model for early firmware (Caliptra FMC + RT, SoC Manifest, MCU RT)
- Secure image retrieval
- Resilient fallback path
- Solution:
- Use a dedicated co-processor with a lightweight network stack
- Automatically configure networking via DHCP
- Securely download Caliptra early firmware images into the Caliptra subsystem
System Architecture
flowchart LR
subgraph Caliptra_Subsystem["Caliptra Subsystem"]
Caliptra["Caliptra"]
RecoveryIF["Recovery I/F"]
MCU_ROM["MCU ROM"]
Mailbox["Mailbox"]
MCU_RT["MCU Runtime"]
Caliptra <--> RecoveryIF
RecoveryIF <--> MCU_ROM
Caliptra <--> Mailbox
Mailbox <--> MCU_RT
end
MCU_ROM <--> Network_ROM["Network ROM<br/>- DHCP Client<br/>- TFTP Client<br/>- FW ID Mapping"]
MCU_RT <--> Network_ROM
Network_ROM <--> Image_Server["Image Server<br/>- Image Store<br/>- DHCP Server<br/>- TFTP Server<br/>- Config File"]
Network Recovery Boot Flow
Stage 1: Booting early firmware
- Network recovery boot initiation
The following diagram illustrates the high-level flow using the
BootSourceProviderinterface, showing howInitiateBootdrives DHCP and TOC fetch:
sequenceDiagram
participant MCU as MCU ROM
participant STG as Staging Memory
participant NET as Network ROM<br>(BootSourceProvider)
participant IMG as Image Server
MCU->>NET: Initiate boot request (flags: FlashWriteBack)
NET->>IMG: Network Configuration Request (DHCPv4/DHCPv6 Discovery)
IMG-->>NET: Network Configuration Response (DHCPv4/DHCPv6 Offer)
NET->>IMG: TFTP GET config file (TOC)
IMG-->>NET: TOC (FW ID mappings)
NET->>NET: Store FW ID to filename mappings
NET-->>MCU: Network boot available
opt FlashWriteBack enabled
MCU->>STG: Initialize staging memory
MCU->>STG: Write flash header and image headers to staging
end
- Early firmware image transfer
Once the boot source is initialized, the MCU ROM uses the BootSourceProvider methods to fetch each firmware component:
sequenceDiagram
participant CRIF as Caliptra Recovery I/F
participant MCU as MCU ROM
participant STG as Staging Memory
participant NET as Network ROM<br>(BootSourceProvider)
participant IMG as Image Server
loop For each image_id (0,1,2)
MCU->>CRIF: Poll recovery readiness
CRIF-->>MCU: Awaiting recovery image_id
MCU->>NET: get_image_metadata(image_id)
NET-->>MCU: ImageMetadata { size, checksum, version }
MCU->>CRIF: Set image size (INDIRECT_FIFO_CTRL.write)
MCU->>NET: download_image(image_id)
NET->>IMG: TFTP GET image file
loop Image transfer by chunk
IMG-->>NET: Image chunk
NET-->>MCU: Forward image chunk (ImageStream::read_chunk)
MCU->>CRIF: Write chunk to recovery I/F
opt FlashWriteBack enabled
MCU->>STG: Write chunk to staging
end
MCU-->>NET: Chunk ACK
end
end
MCU->>NET: Finalize network boot
Stage 2: Booting remainder firmware
This section describes the flow for loading and authenticating SoC images at runtime through the MCU Runtime. The MCU Runtime coordinates image authorization with the Caliptra Core, while Network ROM handles downloading image data from the network.
- Network recovery boot initiation
sequenceDiagram
participant MCURT as MCU RT
participant STG as Staging Memory
participant NET as Network ROM<br>(BootSourceProvider)
participant IMG as Image Server
MCURT->>NET: Initiate boot request (flags: FlashWriteBack, FlashCommitPolicy)
opt If TOC not already cached
NET->>IMG: TFTP GET config file (TOC)
IMG-->>NET: TOC (FW ID mappings)
NET->>NET: Store FW ID to filename mappings
end
NET-->>MCURT: Network boot available
opt FlashWriteBack enabled AND staging not yet initialized
MCURT->>STG: Initialize staging memory
end
- Remainder firmware images transfer
sequenceDiagram
participant CORE as Caliptra RT
participant MCURT as MCU RT
participant STG as Staging Memory
participant PARTA as Active Flash Partition
participant NET as Network ROM<br>(BootSourceProvider)
participant IMG as Image Server
loop For each SoC image(image_id)
MCURT->>CORE: get_image_info(image_id)
CORE-->>MCURT: ImageInfo { load_address }
MCURT->>NET: get_image_metadata(image_id)
NET-->>MCURT: ImageMetadata { size, checksum, version }
MCURT->>NET: download_image(image_id)
NET->>IMG: TFTP GET image file
loop Image transfer by chunk
IMG-->>NET: Image chunk
NET-->>MCURT: Image chunk
MCURT->>MCURT: Write chunk to load_address
opt FlashWriteBack enabled
MCURT->>STG: Write chunk to staging
end
MCURT-->>NET: Chunk ACK
end
MCURT->>CORE: authorize(image_id)
CORE-->>MCURT: Success/Error response
end
MCURT->>NET: Finalize network boot
opt FlashWriteBack enabled (per FlashCommitPolicy)
MCURT->>MCURT: Integrity check staging memory
MCURT->>PARTA: Commit staging to Active Flash Partition
end
Flash Write-Back During Network Recovery
Flash write-back is an optional feature for flash-based systems (e.g., BMC) that persists firmware images downloaded during network recovery to the active flash partition. Without this feature, downloaded images exist only in volatile state β loaded into Caliptra/SoC via recovery I/F or load addresses β and a reboot would require another network recovery. When enabled, each image chunk received from the network is dual-written to both the primary destination (recovery I/F or load address) and a staging memory buffer (vendor choice: DRAM or flash). Once all images are authorized, a lightweight integrity check (header checksums and per-image checksum comparison against TOC metadata) is performed on the staging contents to detect write corruption β this is not a full cryptographic re-verification since the images were already verified by Caliptra on the primary path. The staging contents are then committed to the active flash partition using the same Invalid β copy β Valid partition status transition as the existing firmware update flow. Unlike firmware update (which writes to the inactive partition for safe rollback), network recovery targets the active partition because it has already failed β there is nothing valuable to preserve, and writing directly to it ensures the next boot succeeds immediately without fallback logic or partition pointer swap.
Configuration
Flash write-back is controlled by two fields in the Initiate Boot Request Flags (offset 8):
| Bits | Field | Description |
|---|---|---|
| 0 | FlashWriteBack | 0=Disabled (default), 1=Enabled |
| 1 | FlashCommitPolicy | When to commit staging memory β active flash partition |
| 2-31 | Reserved | Must be 0 |
FlashCommitPolicy values:
| Value | Name | When Commit Occurs |
|---|---|---|
| 0 | PostAuthorization | After all images are cryptographically authorized (default) |
| 1 | PostBootSuccess | After platform signals full boot success |
The commit policy is a vendor policy decision:
- PostAuthorization (recommended default) β all images have been cryptographically verified by Caliptra. In Stage 1, early firmware (FMC+RT, SoC Manifest, MCU RT) is verified during the Caliptra recovery boot process. In Stage 2, each SoC image is explicitly authorized by Caliptra RT. The commit to flash occurs after the last authorization succeeds, at which point the flash image is complete and fully authenticated. This aligns with the existing firmware update verification and commit flow.
- PostBootSuccess is more conservative β the entire system has successfully booted from the recovered images before the flash is updated. This provides the strongest guarantee that the committed image set is fully functional, but widens the window for power-loss before commit and couples flash commit to runtime behavior beyond image integrity.
Flash write-back can also be controlled as a build-time feature flag (flash-writeback) to compile out the staging code path entirely on platforms without writable flash.
Error Handling
- If any staging memory write fails during streaming, the flash write-back is abandoned β the primary boot continues normally. The staging data is discarded.
- If the integrity check fails after streaming, the staging data is not committed. The partition table is unchanged.
- If commit (staging β active flash copy) fails, the active flash partition is left marked
Invalid. - In all failure cases, the network recovery boot itself is not aborted β flash write-back is best-effort and does not gate the primary recovery path.
Protocol Support
The boot source provider supports a minimal set of protocols optimized for the Caliptra ROM environment:
DHCP (Dynamic Host Configuration Protocol)
- Purpose: Automatic network configuration
- Advantages:
- Standard network configuration protocol
- Minimal overhead for basic IP assignment
- Simple UDP-based protocol
- Implementation: Client-side DHCP for IP address, gateway, and boot server discovery
TFTP (Trivial File Transfer Protocol)
- Purpose: Lightweight file transfer for firmware images
- Advantages:
- Extremely lightweight - minimal overhead perfect for ROM environments
- Simple UDP-based protocol - easy to implement securely
- Small code footprint (~5-10KB implementation)
- Standard protocol for network boot scenarios
- Implementation: Client-side TFTP for firmware image download
IPv4 and IPv6 Support
- Dual-Stack: Support both IPv4 and IPv6 throughout discovery and transfer
- UDPv4/UDPv6: TFTP runs over UDP; ensure lwIP
IPv6andUDPare enabled - DHCPv4/DHCPv6: Acquire network configuration via DHCP for both families
- Address Selection: Prefer IPv6 when available; fall back to IPv4
- TOC URLs: TOC entries may reference IPv4 or IPv6 hosts; support
tftp://[IPv6]URLs
DHCP Options for TFTP
- DHCPv4 (RFC 2132)
- Option 66: TFTP server name (hostname or IP address)
- Option 67: Bootfile name (path to TOC or image)
- Optional: Option 43 (Vendor-Specific) for custom parameters
- DHCPv6 (RFC 5970)
- Option 59: Bootfile URL (e.g.,
tftp://server/path/to/toc.bin) - Option 60: Bootfile Parameters (optional, for additional metadata)
- Note: DHCPv6 does not define a separate TFTP server option; use Bootfile URL
- Option 59: Bootfile URL (e.g.,
Boot Source Provider Interface
The Boot Source Provider Interface defines a generic contract for boot image providers, enabling support for multiple boot sources (network boot coprocessor, flash device, or other custom implementations). The MCU ROM communicates with any boot source through this unified interface.
Messaging Protocol
The boot source provider communication uses a simple request-response messaging protocol, with the MCU ROM initiating requests and the BootSourceProvider responding. The following section defines the message types, packet formats, and field definitions.
Message Types and Packet Formats
1. Initiate Boot Request
Initiates the boot source discovery process.
Request Packet:
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Message Type | 0x01 - InitiateBoot |
| 1 | 3 | Reserved | Must be 0 |
| 4 | 4 | Protocol Version | Version of the messaging protocol |
| 8 | 4 | Flags | Bit 0: FlashWriteBack (dual-write streamed images to staging memory), Bit 1: FlashCommitPolicy (0=PostAuthorization, 1=PostBootSuccess), Bits 2-31: Reserved |
| 12 | N | Source Specific | Source-specific initialization parameters |
Response Packet:
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Message Type | 0x81 - InitiateBoot Response |
| 1 | 1 | Status | 0x00=Started, 0x01=InProgress, 0x02β0xFF=Error code |
| 2 | 2 | Reserved | Must be 0 |
2. Get Image Metadata Request
Queries metadata about a specific firmware image.
Request Packet:
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Message Type | 0x02 - Image Info Request |
| 1 | 1 | Firmware ID | 0=CaliptraFmcRt, 1=SocManifest, 2=McuRt, 0x10000000..-SoC |
| 2 | 2 | Reserved | Must be 0 |
Response Packet:
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Message Type | 0x82 - Image Info Response |
| 1 | 1 | Status | 0x00=Success, non-zero=Error |
| 2 | 2 | Reserved | Must be 0 |
| 4 | 4 | Image Size | Total size in bytes |
| 8 | 32 | Checksum | Checksum of the image |
| 40 | 4 | Version | Image version number |
| 44 | 4 | Flags | Bit 0: Compressed, Bit 1: Signed, etc. |
| 48 | 4 | Reserved | For future use |
3. Image Download Request
Initiates download of a firmware image. The image is transferred in chunks. The MCU ROM and BootSourceProvider should share a fixed CHUNK_SIZE configuration to indicate the maximum number of bytes that can be transferred in a chunk.
Request Packet:
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Message Type | 0x03 - Image Download Request |
| 1 | 1 | Firmware ID | 0=CaliptraFmcRt, 1=SocManifest, 2=McuRt, 0x10000000..-SoC |
| 2 | 2 | Reserved | Must be 0 |
| 4 | 4 | Reserved | Can be extended to support flash-based boot |
| 8 | 4 | Reserved | Can be extended to support flash-based boot |
Response Packet (per chunk):
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Message Type | 0x83 - Image Chunk |
| 1 | 1 | Status | 0x00=Success, non-zero=Error |
| 2 | 2 | Sequence Number | For ordered delivery |
| 4 | 4 | Offset | Current byte offset in image |
| 8 | 4 | Chunk Size | Size of data in this chunk. If value is less than CHUNK_SIZE, then this is the last chunk. |
| 12 | N | Image Data | Chunk payload (size = Chunk Size field) |
4. Chunk Acknowledgment
Acknowledges receipt of an image chunk and provides flow control.
Request Packet:
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Message Type | 0x04 - Chunk ACK |
| 1 | 1 | Firmware ID | Firmware being transferred |
| 2 | 2 | Sequence Number | Sequence number to be acknowledged |
| 4 | 4 | Reserved | |
| 8 | 4 | Flags | Bit 0: Ready for next, Bit 1: Error detected |
5. Finalize
Notifies the boot source of recovery completion or error. This allows the BootSourceProvider to free up any allocated resources, terminate connections and stop services (if any).
Request Packet:
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Message Type | 0x05 - Finalize |
| 1 | 1 | Status | 0x00=Success, non-zero=Error |
| 2 | 2 | Error Code | Specific error code if Status != 0 |
| 4 | 4 | Reserved | For future use |
Response Packet:
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Message Type | 0x85 - Finalize ACK |
| 1 | 1 | Status | 0x00=Acknowledged, non-zero=Error |
| 2 | 2 | Reserved | Must be 0 |
| 4 | 4 | Cleanup Flags | Bit 0: Clear TOC, Bit 1: Reset connection |
| 8 | 4 | Reserved | For future use |
Message Summary Table
| Message Type | Code | Direction | Purpose |
|---|---|---|---|
| Initiate Boot Request | 0x01 | MCU β Source | Initiate boot source discovery |
| Initiate Boot Response | 0x81 | Source β MCU | Confirm discovery and image availability |
| Image Metadata Request | 0x02 | MCU β Source | Query image metadata |
| Image Metadata Response | 0x82 | Source β MCU | Return image metadata and checksums |
| Image Download Request | 0x03 | MCU β Source | Start image transfer |
| Image Chunk | 0x83 | Source β MCU | Send image data chunk |
| Chunk ACK | 0x04 | MCU β Source | Acknowledge chunk and flow control |
| Finalize | 0x05 | MCU β Source | Notify recovery completion/error |
| Finalize ACK | 0x85 | Source β MCU | Final acknowledgment |
Error Codes
Error Code Description
---------- -----------
0x00 Success / No Error
0x01 Invalid Message Type
0x02 Invalid Firmware ID
0x03 Image Not Found / Not Available
0x04 Checksum Mismatch
0x05 Transfer Timeout
0x06 Source Not Ready
0x07 Invalid Parameters
0x08 Corrupted Data
0x09 Insufficient Space
0x0A Checksum Verification Failed
0x0B Flash Write Failed
0x0C Flash Staging Not Available
0x0D Flash Commit Failed
0x0E Flash Verification Failed
0xFF Unknown / Unspecified Error
Core Operations
Boot source providers implement the following core operations:
Initialization
- Source Initialization: Initialize the boot source and make it ready for image requests
- Status Discovery: Determine availability and readiness of the boot source
- Configuration Discovery: Discover firmware image metadata and availability
Image Provisioning
- Image Metadata Query: Query information about available firmware images (size, checksums, etc.)
- Image Download: Download firmware images by firmware ID
- Data Streaming: Stream image data to the MCU ROM for direct transfer to Caliptra SS
Supported Firmware IDs
- ID 0: Caliptra FMC+RT image
- ID 1: SoC Manifest
- ID 2: MCU RT image
- ID 0x10000000 - 0x1FFFFFFF: Reserved for SoC Images (range supports up to 268,435,456 distinct SoC image IDs)
Boot Source Provider Interface
#![allow(unused)] fn main() { /// Boot configuration flags passed to initiate_boot #[derive(Debug, Clone, Copy, Default)] pub struct BootFlags { /// When true, streamed image chunks are also dual-written to staging memory pub flash_writeback: bool, /// Controls when staging memory is committed to active flash partition pub flash_commit_policy: FlashCommitPolicy, } /// Defines when staging memory is committed to the active flash partition (vendor policy) #[derive(Debug, Clone, Copy, Default)] pub enum FlashCommitPolicy { /// Commit after all images are cryptographically verified/authorized (recommended) #[default] PostAuthorization = 0, /// Commit after the platform signals full boot success PostBootSuccess = 1, } /// Generic boot source provider interface for the MCU ROM /// This interface abstracts different boot sources (network, flash, etc.) pub trait BootSourceProvider { type Error; /// Initialize the boot source /// This performs source-specific initialization (e.g., DHCP for network, etc.) /// `flags` controls optional behaviors like flash write-back fn initiate_boot(&mut self, flags: BootFlags) -> Result<BootSourceStatus, Self::Error>; /// Get information about a firmware image fn get_image_metadata(&self, firmware_id: FirmwareId) -> Result<ImageInfo, Self::Error>; /// Download firmware image by ID /// Returns a stream for reading image data in chunks fn download_image(&mut self, firmware_id: FirmwareId) -> Result<ImageStream, Self::Error>; /// Get boot source status and capabilities fn get_boot_source_status(&self) -> Result<BootSourceStatus, Self::Error>; /// Deinitialize the boot source fn finalize(&self) -> Result<BootSourceStatus, Self::Error>; } /// Firmware ID enumeration #[derive(Debug, Clone, Copy)] pub enum FirmwareId { /// Caliptra FMC+RT image CaliptraFmcRt = 0, /// SoC Manifest SocManifest = 1, /// MCU RT image McuRt = 2, /// SoC Image (raw u32 value in range 0x10000000 - 0x1FFFFFFF) SocImage(u32), } /// Boot source initialization and capability status #[derive(Debug)] pub struct BootSourceStatus { pub ready: bool, pub initialized: bool, pub config_available: bool, pub available_images: Vec<u32>, } /// Metadata for a firmware image #[derive(Debug, Clone)] pub struct ImageInfo { pub firmware_id: FirmwareId, pub size: u64, pub checksum: Option<[u8; 32]>, pub version: Option<u32>, pub metadata: Vec<u8>, } /// Streaming interface for image data pub trait ImageStream { /// Read next chunk of image data fn read_chunk(&mut self, buffer: &mut [u8]) -> Result<usize, Error>; /// Get total image size if known fn total_size(&self) -> Option<u64>; /// Check if stream is complete fn is_complete(&self) -> bool; } }
Implementation Example: Network Boot Coprocessor
For a network boot coprocessor implementation, the boot source provider would:
- Initialize: Perform DHCP discovery, locate TFTP server, download TOC
- Get Image Metadata: Query image metadata from downloaded TOC
- Download Image: Fetch image from TFTP server and stream to MCU ROM
#![allow(unused)] fn main() { /// Network-based boot source provider implementation pub struct NetworkBootSource { dhcp_client: DhcpClient, tftp_client: TftpClient, toc: TableOfContents, } impl BootSourceProvider for NetworkBootSource { type Error = NetworkBootError; fn initiate_boot(&mut self, _flags: BootFlags) -> Result<BootSourceStatus, Self::Error> { // 1. Perform DHCP discovery self.dhcp_client.discover()?; // 2. Download TOC via TFTP self.toc = self.tftp_client.download_config()?; Ok(BootSourceStatus { ready: true, initialized: true, config_available: true, available_images: self.toc.firmware_mappings.keys().copied().collect(), }) } fn get_image_metadata(&self, firmware_id: FirmwareId) -> Result<ImageInfo, Self::Error> { let mapping = self.toc.get_mapping(firmware_id)?; Ok(ImageInfo { firmware_id, size: mapping.size, checksum: mapping.checksum, version: mapping.version.clone(), }) } fn download_image(&mut self, firmware_id: FirmwareId) -> Result<ImageStream, Self::Error> { let mapping = self.toc.get_mapping(firmware_id)?; self.tftp_client.get_file(&mapping.filename) } fn get_boot_source_status(&self) -> Result<BootSourceStatus, Self::Error> { // Return current network and TFTP status Ok(BootSourceStatus { ready: self.tftp_client.is_reachable(), initialized: true, config_available: true, available_images: self.toc.firmware_mappings.keys().copied().collect(), }) } fn finalize(&self) -> Result<BootSourceStatus, Self::Error> { Ok(BootSourceStatus::Success) } } }
Usage Example
#![allow(unused)] fn main() { // Example: MCU ROM boot process using generic boot source fn recovery_boot(mut boot_source: &mut dyn BootSourceProvider) -> Result<(), Error> { // 1. Initialize boot source let status = boot_source.initiate_boot()?; if !status.ready || !status.initialized { return Err(Error::BootSourceNotAvailable); } // 2. Download each firmware image for firmware_id in [FirmwareId::CaliptraFmcRt, FirmwareId::SocManifest, FirmwareId::McuRt] { // Get image metadata let image_info = boot_source.get_image_metadata(firmware_id)?; // Set up recovery interface with image size set_recovery_image_size(image_info.size)?; // Download image let mut stream = boot_source.download_image(firmware_id)?; // Stream image chunks to recovery interface load_image_stream(stream, ImageDestination::Recovery)?; } // 3. Finalize recovery boot_source.finalize()?; Ok(()) } fn load_image_stream(mut stream: ImageStream, dest: ImageDestination) -> Result<(), Error> { let mut buffer = [0u8; 4096]; while !stream.is_complete() { let bytes_read = stream.read_chunk(&mut buffer)?; if bytes_read > 0 { write_image_chunk(dest, &buffer[..bytes_read])?; } } Ok(()) } }
Configuration File Format (TOC - Table of Contents)
During network boot, the coprocessor first downloads a Table of Contents (TOC) configuration file. This file maps firmware IDs to their corresponding filenames and metadata, allowing the boot process to locate and retrieve the correct firmware images. The TOC follows the same format used for FLASH storage, as described in the Flash Layout specification.
Network Stack Implementation
lwIP (Lightweight IP) will be used with Rust bindings/wrappers to support DHCP and TFTP
Repository: https://git.savannah.nongnu.org/cgit/lwip.git
Additional Resources
The following documents are available across the Caliptra project repositories. Links point to the version-appropriate reference when available.
Hardware
- VeeR EL2 Programmer's Reference Manual β Latest documentation for the VeeR EL2 core
- Caliptra Subsystem Overview β Required dependencies, env variables, repository overview, simulation flow and regression tests
- TileLink-UL Bus Specification β Bus specification for comportable devices
- TileLink-UL Protocol Checker β Protocol checked description for the TileLink-UL bus
- TileLink-UL XBAR DV β TileLink-UL bus testing overview
- Power Manager Theory of Operation β Overview of Power Manager's functionality
- Power Manager Programmer's Guide
- OTP Controller Field Descriptions β Description of fields stored in the OTP memory
- OTP Controller Registers
- OTP Controller Partitions β Description of OTP partition attributes
- OTP Controller Digests
- Analog Sensor Top Technical Specification
- Analog Sensor Top Interface Signals
- Caliptra Hands-On Guide β Required dependencies, env variables, repository overview, simulation flow and regression tests for caliptra-rtl
- UART HWIP Technical Specification β Specification, overview of the functionality and a programmer's guide
- Internal Registers for caliptra-rtl β Latest register description for caliptra-rtl components
- FV ECC Block Overview
- JTAG DPI module for OpenOCD remote_bitbang driver β Overview of a JTAG over DPI library
- UART DV β UART testing overview
- ECC β ECC proofs
- SHA256 β SHA256 testing overview
- SHA512 β SHA512 testing overview
- SHA512_MASKED β SHA512_MASKED testing overview
- HMAC β HMAC testing overview
- DOE β DOE testing overview
- HMAC DRBG β HMAC DRGB testing overview
- Adam's Bridge Hands-On Guide β Required dependencies, env variables, repository overview, simulation flow and regression tests
- Threat Model for Securing Adams Bridge Against Side-Channel Attacks
- Caliptra β Readme of the Caliptra project
- Hardware Release Process
Software
- Caliptra firmware and software β Directory structure, building and testing for caliptra-sw
- Caliptra FMC Test Coverage β Description of FMC test cases
- Caliptra Runtime Firmware Test Coverage β Describes test cases
- Caliptra ROM Errors β Fatal and non-fatal error codes description
- Caliptra ROM Thread Model β Overview of rules to ensure minimal possibility of security issues
- Caliptra ROM Test Coverage β Describes test cases
- Generating Register Definitions β Instructions for generating register definitions from caliptra-rtl
- Emulator for Caliptra β Emulator's class and state diagrams
- Caliptra C API - libcaliptra
- Caliptra C API Examples β Example on how to interact with the Caliptra API and adapt it to the desired target
- Caliptra C API Examples - hwmodel β Example implementation of libcaliptra's hardware interface
- Caliptra Error Codes β Describes where Caliptra error codes are defined
- C and Rust bindings for Caliptra RTL (verilated) β Building and running C and Rust bindings for a verilated model of caliptra-rtl
- Caliptra Core FPGA Guide β Guide for building and running caliptra-rtl on an FPGA
- Caliptra Subsystem FPGA Guide β Guide for building and running caliptra-ss on an FPGA
- Caliptra SW Tests
- FIPS Functional Test Suite β Overview of the test suite and available test cases
- Caliptra DPE β General overview of Caliptra DPE
- Caliptra DPE Verification Tests β Description of DPE tests
- Caliptra DPE Simulator β Overview of the DPE simulator
- Caliptra MCU firmware and software β Overview of caliptra-mcu-sw
Integration
Release Notes
- Release Notes - Caliptra Subsystem
- Release Notes - Caliptra RTL
- Release Notes - Adam's Bridge
- Release Notes - VeeR EL2
Tools
- fuse_ctrl Partitions Generator β A script for generation of configurable blocks for the fuse_ctrl instantiation
- Caliptra fpga-boss β Helper utility used for running Caliptra firmware on ZCU104 FPGA
- Caliptra GitHub GCP Runner Infrastructure β Overview of the CI runner architecture
- file-header-fix β Utility used to ensure that all files have proper copyright headers
Governance
- Contributing to Caliptra β Guidelines for contributing to the project
- Workgroup Charter for Caliptra β A formal charter of the Caliptra CHIPS Alliance Workgroup
- Caliptra Project Security Incident Response
- Caliptra Technical Advisory Committee (TAC) Members
- Caliptra Security Assessment Report
- Caliptra Trademark Usage Policy
- Caliptra Trademark Audit Process
- Caliptra Compliant Brand Guidelines
- GitHub Rules
- Caliptra Release Checklist β Describes the release creation process
- Caliptra 2.0 Branching Strategy β Branch naming convention