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.

The Runtime fuse API provides authorized MCU mailbox operations for reading and writing OTP and transport-neutral commands for supported provisioning and lock operations.

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:

  1. Defining how many bits are backed by actual fuses in each field
  2. 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 may have only 4 bits backed by actual fuses in hardware. The additional fuse map defines how many bits are available in each field because this information is vendor-specific and is 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}
  ],
  // 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 generation

The implemented generator runs as:

cargo xtask registers-autogen --fuses-hjson <path> --otp-mmap-hjson <path>

Both path options are optional and default to hw/fuses.hjson and the Caliptra subsystem OTP memory map. The command generates:

  • Rust fuse definitions in the selected generated-register output
  • The corresponding fuse_map.md

Fuse Provisioning Commands

MCU Runtime implements generic fuse read, write, and partition-lock operations through the MCI mailbox. Supported provisioning and lock operations are also exposed through the common SPDM VDM command interface.

The commands are:

  • Write
  • Read
  • Lock partition

Authorization

The reference Runtime routes these operations through its generic authorized command handler. The requester first obtains a one-use MACC challenge and then supplies the configured ECC P-384 and ML-DSA-87 public keys plus hybrid signatures over the command payload and challenge. See Authorization-Gated Subcommand Wrapper.

These commands reference partition, fuse, and bit numbers that must use the same numbers generated by Fuse definition generation.

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.hjson and vendor_fuses.hjson files.

MC_FUSE_READ

Reads fuse values.

Command Code: 0x4946_5052 ("IFPR")

Table: MC_FUSE_READ input arguments

NameTypeDescription
chksumu32
partitionu32Partition number to read from
entryu32Entry to read

Table: MC_FUSE_READ output arguments

NameTypeDescription
chksumu32
fips_statusu32FIPS approved or an error
length (bits)u32Number of bits that are valid
datau8[...]Fuse data (length/8)

MC_FUSE_WRITE

Write fuse values.

Command Code: 0x4946_5057 ("IFPW")

Table: MC_FUSE_WRITE input arguments

NameTypeDescription
chksumu32
word_addru32Entry to write (word offset)
datau32Word to write
masku32Bit-Mask to only write specified bits

Table: MC_FUSE_WRITE output arguments

NameTypeDescription
chksumu32
fips_statusu32FIPS 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.
  • Bits cleared in mask are ignored
  • 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

NameTypeDescription
chksumu32
partitionu32Partition number to lock

Table: MC_FUSE_LOCK_PARTITION output arguments

NameTypeDescription
chksumu32
fips_statusu32FIPS 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.

MC_PROVISION_VENDOR_PK_HASH

Provision a new vendor PK hash.

Command Code: 0x5056_504b ("PVPK")

Table: MC_PROVISION_VENDOR_PK_HASH input arguments

NameTypeDescription
chksumu32
slotu32The vendor PK hash slot to use
hash[u8; 48]New vendor PK hash

Table: MC_PROVISION_VENDOR_PK_HASH output arguments

NameTypeDescription
chksumu32
fips_statusu32FIPS approved or an error

Caveats:

  • Fails if the slot already contains data

MC_PROVISION_OWNER_PK_HASH

Provision CPTRA_SS_OWNER_PK_HASH using its 48-byte dword-reversed OTP representation.

Command Code: 0x504F_504B ("POPK")

Table: MC_PROVISION_OWNER_PK_HASH input arguments

NameTypeDescription
chksumu32
hash[u8; 48]New owner public-key hash

Table: MC_PROVISION_OWNER_PK_HASH output arguments

NameTypeDescription
chksumu32
fips_statusu32FIPS approved or an error

Caveats:

  • The all-zero hash is rejected.
  • Reprovisioning the identical hash is idempotent.
  • Provisioning a different hash after any owner-hash data has been burned is rejected.
  • After verifying the hash, the command burns and verifies bit 0 of CPTRA_SS_OWNER_PK_HASH_VALID for use as a commit marker by ROM versions that enforce it.
  • Current MCU ROM does not check CPTRA_SS_OWNER_PK_HASH_VALID before consuming the hash. Provisioning must not be interrupted by reset or power loss; an interruption can cause ROM to consume a partial hash and prevent retrying the intended hash.
  • The newly provisioned owner hash is consumed by MCU ROM on the next reset.

MC_FUSE_REVOKE_VENDOR_PUB_KEY

Revoke one vendor firmware verification key within a vendor PK hash slot.

Command Code: 0x4D52_564B ("MRVK")

Table: MC_FUSE_REVOKE_VENDOR_PUB_KEY input arguments

NameTypeDescription
chksumu32
reservedu32Reserved; must be zero
vendor_pk_hash_slotu32Vendor PK hash slot containing the key to revoke
key_typeu320 = ECDSA P-384, 1 = LMS, 2 = MLDSA-87
key_indexu32Key index within the selected key type's revocation field

Table: MC_FUSE_REVOKE_VENDOR_PUB_KEY output arguments

NameTypeDescription
chksumu32
fips_statusu32FIPS approved or an error

Caveats:

  • This command must be authorized.
  • The selected PK hash slot must be provisioned and valid.
  • The command fails if it targets the key used to boot the currently running firmware.
  • The last key index for a key type cannot be revoked.

MC_FUSE_REVOKE_VENDOR_PK_HASH

Revoke a vendor PK hash. Marks a vendor PK hash as invalid, revoking all of the associated keys.

Command Code: 0x5256_4b48 ("RVKH")

Table: MC_FUSE_REVOKE_VENDOR_PK_HASH input arguments

NameTypeDescription
chksumu32
vendor_pk_hash_slotu32Vendor PK hash slot to revoke

Table: MC_FUSE_REVOKE_VENDOR_PK_HASH output arguments

NameTypeDescription
chksumu32
fips_statusu32FIPS approved or an error

Caveats:

  • This command must be authorized.
  • This command is idempotent, so that revoking a slot twice has no effect.
  • Trying to revoke an empty slot will result in an error
  • Trying to revoke the PK hash slot used to boot the currently running firmware will result in an error

Field Entropy Provisioning

Field entropy can only be programmed by Caliptra. MCU Runtime exposes the authorized MC_FE_PROG (MCFP) command, which accepts one partition index and forwards Caliptra's FE_PROG mailbox command. MCU ROM can also program the partitions selected by RomParameters::program_field_entropy during cold boot; that path records Started and Finished state in FIELD_ENTROPY_STATE and rejects partial or zeroized slots.

The reference DOT configuration derives its effective key from the stable IDevID root, so Field Entropy programming does not invalidate the DOT_BLOB. RomParameters::dot_stable_key_type can select LDevID for ROM paths, but the reference Runtime always derives DOT keys from IDevID and no implementation re-HMACs an existing DOT_BLOB when Field Entropy changes. The LDevID override is therefore not compatible with the reference Runtime DOT commands or an existing IDevID-sealed blob without corresponding platform code changes.

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.

Terminology note: the current OneHot* layout names are existing schema/API names. They do not mean true one-hot encoding, where exactly one bit is set. They implement monotonic bit-count, or thermometer-style, counters: the logical value is the number of set bits, and writers encode value n as (1 << n) - 1.

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

Bit-count counter (OneHot layout name)

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 bit.

The values can span multiple u32 words but the result is always a single u32 count value.

Example:

  • 0b0000 → 0
  • 0b0001 → 1
  • 0b0011 → 2
  • 0b0111 → 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 votes 111, bit 1 has votes 110, bit 2 has votes 100)
  • 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 the OneHot bit-count counter layout. First applies majority voting to each duplicated bit, then counts the number of bits set.

This combines redundancy with bit-count counter semantics.

Example: With 3 logical bits and 3x duplication:

  • Raw fuses: 0b100_110_111 (bit 0 has votes 111, bit 1 has votes 110, bit 2 has votes 100)
  • 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 is 0
  • Bit 1: votes are 0,1,1 → majority is 1
  • Bit 2: votes are 1,1,1 → majority is 1
  • Result: [0b110]

This is an alternative to LinearMajorityVote for larger values.

LinearOr

Like LinearMajorityVote, each logical bit is duplicated multiple times. However, the result bit is 1 if any copy is set (bitwise OR instead of majority vote). This is appropriate for OTP bits where a bit failing to flip to 1 is more likely than spontaneously flipping to 0.

Example: With 2-bit logical value and 3x duplication:

  • Raw fuses: 0b000_001_111 (bit 0 has copies 111, bit 1 has copies 001)
  • Result: 0b11 (both bits have at least one copy set)

With majority vote the same raw value would yield 0b01 (bit 1 fails the ≥2 threshold).

OneHotLinearOr

Combines LinearOr with the OneHot bit-count counter layout. First applies OR reduction to each duplicated bit, then counts the number of bits set.

Example: With 3 logical bits and 3x duplication:

  • Raw fuses: 0b001_010_111 (bit 0 has copies 111, bit 1 has copies 010, bit 2 has copies 001)
  • After OR on each bit: 0b111 (3 bits set)
  • Result: 3

This is the recommended way to store incrementable counters and version numbers in OTP without ECC.

WordOr

Entire u32 words are duplicated. Each bit position across the duplicated words is decided by OR (any word having the bit set → result bit is 1).

Example: With 3 duplicated words:

  • Raw fuses: [0b100, 0b010, 0b001]
  • Result: [0b111]

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_LARGE if 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)
  • OR calculations treat any non-zero duplicate as a 1

Default Platform Configuration

The default McuFuseLayoutPolicy uses:

  • Single: For certificate data, identifiers, and validity flags that are assumed to be protected by ECC
  • OneHotLinearOr (3x): For SVN fields and monotonic bit-count counters
  • LinearOr (3x): for single revocation fields, flags, and small bitmasks
  • WordMajorityVote (3x): for larger bitmasks

The SDK Owner SoC Manifest counter uses one OTP bit per SVN increment, without redundant copies. See Owner SoC Manifest SVN for storage and activation.

This provides a balance between redundancy for critical security fields and storage efficiency for larger data structures.