Skip to content

Commit f0d87f5

Browse files
committed
[hw-model] Merge OTP values on cold reset in the subsystem hw-model
Use save-and-restore in cold_reset and bitwise OR loops in `init_otp_with_lc_override` to combine initial settings with current backing memory. This will enable tests that burn fuses during operation. This is especially useful in MCU tests such as revoking firmware verification keys.
1 parent 8391cc3 commit f0d87f5

1 file changed

Lines changed: 49 additions & 28 deletions

File tree

hw-model/src/model_fpga_subsystem.rs

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,9 @@ impl ModelFpgaSubsystem {
15231523
otp_data.len(),
15241524
))?;
15251525
}
1526-
otp_data[..self.otp_init.len()].copy_from_slice(&self.otp_init);
1526+
for i in 0..self.otp_init.len() {
1527+
otp_data[i] |= self.otp_init[i];
1528+
}
15271529
}
15281530

15291531
// Determine the LC state: explicit override takes priority.
@@ -1544,21 +1546,27 @@ impl ModelFpgaSubsystem {
15441546
println!("Provisioning lifecycle partition (State: {}).", lc_state);
15451547
let mem = lc_generate_memory(lc_state, 1)?;
15461548
let offset = OTP_LIFECYCLE_PARTITION_OFFSET;
1547-
otp_data[offset..offset + mem.len()].copy_from_slice(&mem);
1549+
for i in 0..mem.len() {
1550+
otp_data[offset + i] |= mem[i];
1551+
}
15481552
}
15491553

15501554
// Provision default LC tokens.
15511555
println!("Provisioning SECRET_LC_TRANSITION partition.");
15521556
let tokens = &DEFAULT_LIFECYCLE_RAW_TOKENS;
15531557
let mem = otp_generate_lifecycle_tokens_mem(tokens)?;
15541558
let offset = OTP_SECRET_LC_TRANSITION_PARTITION_OFFSET;
1555-
otp_data[offset..offset + mem.len()].copy_from_slice(&mem);
1559+
for i in 0..mem.len() {
1560+
otp_data[offset + i] |= mem[i];
1561+
}
15561562

15571563
// Provision default SW_TEST_UNLOCK partition (manuf debug unlock token).
15581564
println!("Provisioning SW_TEST_UNLOCK partition.");
15591565
let mem = otp_generate_manuf_debug_unlock_token_mem(&DEFAULT_MANUF_DEBUG_UNLOCK_RAW_TOKEN)?;
15601566
let offset = OTP_SW_TEST_UNLOCK_PARTITION_OFFSET;
1561-
otp_data[offset..offset + mem.len()].copy_from_slice(&mem);
1567+
for i in 0..mem.len() {
1568+
otp_data[offset + i] |= mem[i];
1569+
}
15621570

15631571
// Provision default SW_MANUF partition.
15641572
// TODO(timothytrippel): enable provisioning prod debug unlock public key hashes for public
@@ -1587,7 +1595,9 @@ impl ModelFpgaSubsystem {
15871595
..Default::default()
15881596
})?;
15891597
let offset = OTP_SW_MANUF_PARTITION_OFFSET;
1590-
otp_data[offset..offset + mem.len()].copy_from_slice(&mem);
1598+
for i in 0..mem.len() {
1599+
otp_data[offset + i] |= mem[i];
1600+
}
15911601

15921602
// Provision UDS seed in SECRET_MANUF partition
15931603
println!("Provisioning UDS seed in SECRET_MANUF partition.");
@@ -1598,8 +1608,9 @@ impl ModelFpgaSubsystem {
15981608
.flat_map(|&word| word.to_le_bytes())
15991609
.collect();
16001610
println!("Setting UDS seed to {:x?}", HexSlice(&uds_seed_bytes));
1601-
otp_data[UDS_SEED_OFFSET..UDS_SEED_OFFSET + uds_seed_bytes.len()]
1602-
.copy_from_slice(&uds_seed_bytes);
1611+
for i in 0..uds_seed_bytes.len() {
1612+
otp_data[UDS_SEED_OFFSET + i] |= uds_seed_bytes[i];
1613+
}
16031614

16041615
// Provision field entropy in SECRET_MANUF partition
16051616
println!("Provisioning field entropy in SECRET_MANUF partition.");
@@ -1613,16 +1624,18 @@ impl ModelFpgaSubsystem {
16131624
"Setting field entropy to {:x?}",
16141625
HexSlice(&field_entropy_bytes)
16151626
);
1616-
otp_data[FIELD_ENTROPY_OFFSET..FIELD_ENTROPY_OFFSET + field_entropy_bytes.len()]
1617-
.copy_from_slice(&field_entropy_bytes);
1627+
for i in 0..field_entropy_bytes.len() {
1628+
otp_data[FIELD_ENTROPY_OFFSET + i] |= field_entropy_bytes[i];
1629+
}
16181630

16191631
let vendor_pk_hash = self.fuses.vendor_pk_hash.as_bytes();
16201632
println!(
16211633
"Setting vendor public key hash to {:x?}",
16221634
HexSlice(vendor_pk_hash)
16231635
);
1624-
otp_data[FUSE_VENDOR_PKHASH_OFFSET..FUSE_VENDOR_PKHASH_OFFSET + vendor_pk_hash.len()]
1625-
.copy_from_slice(vendor_pk_hash);
1636+
for i in 0..vendor_pk_hash.len() {
1637+
otp_data[FUSE_VENDOR_PKHASH_OFFSET + i] |= vendor_pk_hash[i];
1638+
}
16261639

16271640
let vendor_pqc_type = FwVerificationPqcKeyType::from_u8(self.fuses.fuse_pqc_key_type as u8)
16281641
.unwrap_or(FwVerificationPqcKeyType::LMS);
@@ -1634,16 +1647,17 @@ impl ModelFpgaSubsystem {
16341647
FwVerificationPqcKeyType::MLDSA => 0,
16351648
FwVerificationPqcKeyType::LMS => 1,
16361649
};
1637-
otp_data[FUSE_PQC_OFFSET] = val;
1650+
otp_data[FUSE_PQC_OFFSET] |= val;
16381651

16391652
// Owner public key hash (48 bytes) lives in VENDOR_HASHES_PROD partition
16401653
let owner_pk_hash = self.fuses.owner_pk_hash.as_bytes();
16411654
println!(
16421655
"Setting owner public key hash to {:x?}",
16431656
HexSlice(owner_pk_hash)
16441657
);
1645-
otp_data[FUSE_OWNER_PKHASH_OFFSET..FUSE_OWNER_PKHASH_OFFSET + owner_pk_hash.len()]
1646-
.copy_from_slice(owner_pk_hash);
1658+
for i in 0..owner_pk_hash.len() {
1659+
otp_data[FUSE_OWNER_PKHASH_OFFSET + i] |= owner_pk_hash[i];
1660+
}
16471661

16481662
// Owner revocation fields (ECC, LMS, MLDSA) in VENDOR_REVOCATIONS_PROD partition
16491663
// Note: ECC revocation in API is a 4-bit value; store in low bits of u32 here.
@@ -1654,41 +1668,42 @@ impl ModelFpgaSubsystem {
16541668
"Setting owner revocations ecc={:#x} lms={:#x} mldsa={:#x}",
16551669
vendor_ecc_revocation, vendor_lms_revocation, vendor_mldsa_revocation
16561670
);
1657-
otp_data[FUSE_VENDOR_ECC_REVOCATION_OFFSET..FUSE_VENDOR_ECC_REVOCATION_OFFSET + 4]
1658-
.copy_from_slice(&vendor_ecc_revocation.to_le_bytes());
1659-
otp_data[FUSE_VENDOR_LMS_REVOCATION_OFFSET..FUSE_VENDOR_LMS_REVOCATION_OFFSET + 4]
1660-
.copy_from_slice(&vendor_lms_revocation.to_le_bytes());
1661-
otp_data[FUSE_VENDOR_REVOCATION_OFFSET..FUSE_VENDOR_REVOCATION_OFFSET + 4]
1662-
.copy_from_slice(&vendor_mldsa_revocation.to_le_bytes());
1671+
for i in 0..4 {
1672+
otp_data[FUSE_VENDOR_ECC_REVOCATION_OFFSET + i] |= vendor_ecc_revocation.to_le_bytes()[i];
1673+
otp_data[FUSE_VENDOR_LMS_REVOCATION_OFFSET + i] |= vendor_lms_revocation.to_le_bytes()[i];
1674+
otp_data[FUSE_VENDOR_REVOCATION_OFFSET + i] |= vendor_mldsa_revocation.to_le_bytes()[i];
1675+
}
16631676

16641677
// Firmware/runtime SVN (16 bytes -> 4 words)
16651678
let fw_svn = self.fuses.fw_svn.as_bytes();
16661679
println!("Setting runtime FW SVN to {:x?}", HexSlice(fw_svn));
1667-
otp_data[OTP_SVN_PARTITION_RUNTIME_SVN_FIELD_OFFSET
1668-
..OTP_SVN_PARTITION_RUNTIME_SVN_FIELD_OFFSET + fw_svn.len()]
1669-
.copy_from_slice(fw_svn);
1680+
for i in 0..fw_svn.len() {
1681+
otp_data[OTP_SVN_PARTITION_RUNTIME_SVN_FIELD_OFFSET + i] |= fw_svn[i];
1682+
}
16701683

16711684
// SoC manifest SVN (16 bytes -> 4 words)
16721685
let soc_manifest_svn = self.fuses.soc_manifest_svn.as_bytes();
16731686
println!(
16741687
"Setting SoC manifest SVN to {:x?}",
16751688
HexSlice(soc_manifest_svn)
16761689
);
1677-
otp_data[OTP_SVN_PARTITION_SOC_MANIFEST_SVN_FIELD_OFFSET
1678-
..OTP_SVN_PARTITION_SOC_MANIFEST_SVN_FIELD_OFFSET + soc_manifest_svn.len()]
1679-
.copy_from_slice(soc_manifest_svn);
1690+
for i in 0..soc_manifest_svn.len() {
1691+
otp_data[OTP_SVN_PARTITION_SOC_MANIFEST_SVN_FIELD_OFFSET + i] |= soc_manifest_svn[i];
1692+
}
16801693

16811694
println!("Provisioning CPTRA_SS_LOCK_HEK_PROD_0 partition.");
16821695
let hek_seed_bytes = self.fuses.hek_seed.as_bytes();
16831696
let offset = OTP_CPTRA_SS_LOCK_HEK_PROD_0_OFFSET;
1684-
otp_data[offset..offset + hek_seed_bytes.len()].copy_from_slice(hek_seed_bytes);
1697+
for i in 0..hek_seed_bytes.len() {
1698+
otp_data[offset + i] |= hek_seed_bytes[i];
1699+
}
16851700

16861701
// Max SOC Manifest SVN (1 byte used)
16871702
println!(
16881703
"Burning fuse for SOC MAX SVN {}",
16891704
self.fuses.soc_manifest_max_svn
16901705
);
1691-
otp_data[OTP_SVN_PARTITION_SOC_MAX_SVN_FIELD_OFFSET] = self.fuses.soc_manifest_max_svn;
1706+
otp_data[OTP_SVN_PARTITION_SOC_MAX_SVN_FIELD_OFFSET] |= self.fuses.soc_manifest_max_svn;
16921707

16931708
self.otp_slice().copy_from_slice(&otp_data);
16941709

@@ -2500,10 +2515,16 @@ impl HwModel for ModelFpgaSubsystem {
25002515
self.realtime_thread_paused.store(true, Ordering::Relaxed);
25012516
std::thread::sleep(Duration::from_millis(2));
25022517

2518+
// Save OTP contents prior to AXI reset as it is zeroized by reset.
2519+
let saved_otp = self.otp_slice().to_vec();
2520+
25032521
// Full AXI reset to clear all subsystem state including MCU
25042522
println!("AXI reset");
25052523
self.axi_reset();
25062524

2525+
// Restore OTP contents after AXI reset
2526+
self.otp_slice().copy_from_slice(&saved_otp);
2527+
25072528
// Re-program all hardware registers cleared by AXI reset
25082529
self.setup_hardware_registers();
25092530

0 commit comments

Comments
 (0)