Repository: chipsalliance/caliptra-sw
Pull request #201 (2023-04-25 18:07:46)

[feat] ROM status reporting


Patch:
diff --git a/common/src/boot_status.rs b/common/src/boot_status.rs
new file mode 100644
index 00000000..33ca1084
--- /dev/null
+++ b/common/src/boot_status.rs
@@ -0,0 +1,64 @@
+/*++
+
+Licensed under the Apache-2.0 license.
+
+File Name:
+
+    boot_status.rs
+
+Abstract:
+
+    ROM boot status codes.
+
+--*/
+
+const IDEVID_BOOT_STATUS_BASE: u32 = 1;
+const LDEVID_BOOT_STATUS_BASE: u32 = 65;
+const FMCALIAS_BOOT_STATUS_BASE: u32 = 129;
+
+/// Statuses used by ROM to log dice derivation progress.
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum RomBootStatus {
+    // Idevid Statuses
+    IDevIdDecryptUdsComplete = IDEVID_BOOT_STATUS_BASE,
+    IDevIdDecryptFeComplete = IDEVID_BOOT_STATUS_BASE + 1,
+    IDevIdClearDoeSecretsComplete = IDEVID_BOOT_STATUS_BASE + 2,
+    IDevIdCdiDerivationComplete = IDEVID_BOOT_STATUS_BASE + 3,
+    IDevIdKeyPairDerivationComplete = IDEVID_BOOT_STATUS_BASE + 4,
+    IDevIdSubjIdSnGenerationComplete = IDEVID_BOOT_STATUS_BASE + 5,
+    IDevIdSubjKeyIdGenerationComplete = IDEVID_BOOT_STATUS_BASE + 6,
+    IDevIdMakeCsrComplete = IDEVID_BOOT_STATUS_BASE + 7,
+    IDevIdSendCsrComplete = IDEVID_BOOT_STATUS_BASE + 8,
+    IDevIdDerivationComplete = IDEVID_BOOT_STATUS_BASE + 9,
+
+    // Ldevid Statuses
+    LDevIdCdiDerivationComplete = LDEVID_BOOT_STATUS_BASE,
+    LDevIdKeyPairDerivationComplete = LDEVID_BOOT_STATUS_BASE + 1,
+    LDevIdSubjIdSnGenerationComplete = LDEVID_BOOT_STATUS_BASE + 2,
+    LDevIdSubjKeyIdGenerationComplete = LDEVID_BOOT_STATUS_BASE + 3,
+    LDevIdCertSigGenerationComplete = LDEVID_BOOT_STATUS_BASE + 4,
+    LDevIdDerivationComplete = LDEVID_BOOT_STATUS_BASE + 5,
+
+    // FmcAlias Statuses
+    FmcAliasDownloadImageComplete = FMCALIAS_BOOT_STATUS_BASE,
+    FmcAliasManifestLoadComplete = FMCALIAS_BOOT_STATUS_BASE + 1,
+    FmcAliasImageVerificationComplete = FMCALIAS_BOOT_STATUS_BASE + 2,
+    FmcAliasPopulateDataVaultComplete = FMCALIAS_BOOT_STATUS_BASE + 3,
+    FmcAliasExtendPcrComplete = FMCALIAS_BOOT_STATUS_BASE + 4,
+    FmcAliasLoadImageComplete = FMCALIAS_BOOT_STATUS_BASE + 5,
+    FmcAliasFirmwareDownloadTxComplete = FMCALIAS_BOOT_STATUS_BASE + 6,
+    FmcAliasDeriveCdiComplete = FMCALIAS_BOOT_STATUS_BASE + 7,
+    FmcAliasKeyPairDerivationComplete = FMCALIAS_BOOT_STATUS_BASE + 8,
+    FmcAliasSubjIdSnGenerationComplete = FMCALIAS_BOOT_STATUS_BASE + 9,
+    FmcAliasSubjKeyIdGenerationComplete = FMCALIAS_BOOT_STATUS_BASE + 10,
+    FmcAliasCertSigGenerationComplete = FMCALIAS_BOOT_STATUS_BASE + 11,
+    FmcAliasDerivationComplete = FMCALIAS_BOOT_STATUS_BASE + 12,
+}
+
+impl From<RomBootStatus> for u32 {
+    /// Converts to this type from the input type.
+    fn from(status: RomBootStatus) -> u32 {
+        status as u32
+    }
+}
diff --git a/common/src/lib.rs b/common/src/lib.rs
index a8d95032..227774ca 100644
--- a/common/src/lib.rs
+++ b/common/src/lib.rs
@@ -7,10 +7,12 @@ pub mod dice;
 pub mod hand_off;
 #[macro_use]
 pub mod printer;
+pub mod boot_status;
 ///merge imports
 pub use hand_off::{
     print_fht, report_handoff_error_and_halt, DataStore, DataVaultRegister, FirmwareHandoffTable,
     HandOffDataHandle, Vault, FHT_INVALID_HANDLE, FHT_MARKER,
 };
 
+pub use boot_status::RomBootStatus;
 pub use printer::MutablePrinter;
diff --git a/rom/dev/src/flow/cold_reset/fmc_alias.rs b/rom/dev/src/flow/cold_reset/fmc_alias.rs
index 32e5dca0..488f6507 100644
--- a/rom/dev/src/flow/cold_reset/fmc_alias.rs
+++ b/rom/dev/src/flow/cold_reset/fmc_alias.rs
@@ -13,8 +13,6 @@ Abstract:
 
 --*/
 
-use core::mem::ManuallyDrop;
-
 use super::crypto::{Crypto, Ecc384KeyPair};
 use super::dice::{DiceInput, DiceLayer, DiceOutput};
 use super::x509::X509;
@@ -25,14 +23,17 @@ use crate::rom_env::RomEnv;
 use crate::verifier::RomImageVerificationEnv;
 use crate::{cprint, cprintln, pcr};
 use caliptra_common::dice;
+use caliptra_common::RomBootStatus::*;
 use caliptra_drivers::{
-    okref, Array4x12, CaliptraResult, ColdResetEntry4, ColdResetEntry48, Hmac384Data, Hmac384Key,
-    KeyId, KeyReadArgs, Lifecycle, MailboxRecvTxn, ResetReason, WarmResetEntry4, WarmResetEntry48,
+    okref, report_boot_status, Array4x12, CaliptraResult, ColdResetEntry4, ColdResetEntry48,
+    Hmac384Data, Hmac384Key, KeyId, KeyReadArgs, Lifecycle, MailboxRecvTxn, ResetReason,
+    WarmResetEntry4, WarmResetEntry48,
 };
 use caliptra_error::caliptra_err_def;
 use caliptra_image_types::{ImageManifest, IMAGE_BYTE_SIZE};
 use caliptra_image_verify::{ImageVerificationInfo, ImageVerifier};
 use caliptra_x509::{FmcAliasCertTbs, FmcAliasCertTbsParams, NotAfter, NotBefore};
+use core::mem::ManuallyDrop;
 use zerocopy::{AsBytes, FromBytes};
 
 extern "C" {
@@ -79,12 +80,14 @@ impl DiceLayer for FmcAliasLayer {
 
         // Extend PCR0
         pcr::extend_pcr0(env)?;
+        report_boot_status(FmcAliasExtendPcrComplete.into());
 
         // Load the image
         Self::load_image(env, manifest, &txn)?;
 
         // Complete the mailbox transaction indicating success.
         txn.complete(true)?;
+        report_boot_status(FmcAliasFirmwareDownloadTxComplete.into());
 
         // At this point PCR0 & PCR1 must have the same value. We use the value
         // of PCR1 as the measurement for deriving the CDI
@@ -97,13 +100,17 @@ impl DiceLayer for FmcAliasLayer {
 
         // Derive DICE Key Pair from CDI
         let key_pair = Self::derive_key_pair(env, KEY_ID_CDI, KEY_ID_FMC_PRIV_KEY)?;
+        report_boot_status(FmcAliasKeyPairDerivationComplete.into());
 
         // Generate the Subject Serial Number and Subject Key Identifier.
         //
         // This information will be used by next DICE Layer while generating
         // certificates
         let subj_sn = X509::subj_sn(env, &key_pair.pub_key)?;
+        report_boot_status(FmcAliasSubjIdSnGenerationComplete.into());
+
         let subj_key_id = X509::subj_key_id(env, &key_pair.pub_key)?;
+        report_boot_status(FmcAliasSubjKeyIdGenerationComplete.into());
 
         // Generate the output for next layer
         let output = DiceOutput {
@@ -137,6 +144,7 @@ impl DiceLayer for FmcAliasLayer {
         // Generate Local Device ID Certificate
         Self::generate_cert_sig(env, input, &output, &nb.not_before, &nf.not_after)?;
 
+        report_boot_status(FmcAliasDerivationComplete.into());
         cprintln!("[afmc] --");
 
         Ok(output)
@@ -165,7 +173,6 @@ impl FmcAliasLayer {
 
         cprint!("[afmc] Waiting for Image ");
         loop {
-            cprint!(".");
             if let Some(mut txn) = env.mbox().map(|m| m.try_start_recv_txn()) {
                 if txn.cmd() != Self::MBOX_DOWNLOAD_FIRMWARE_CMD_ID {
                     cprintln!("Invalid command 0x{:08x} received", txn.cmd());
@@ -183,6 +190,7 @@ impl FmcAliasLayer {
 
                 cprintln!("");
                 cprintln!("[afmc] Received Image of size {} bytes" txn.dlen());
+                report_boot_status(FmcAliasDownloadImageComplete.into());
                 break Ok(txn);
             }
         }
@@ -201,7 +209,12 @@ impl FmcAliasLayer {
 
         txn.copy_request(slice)?;
 
-        ImageManifest::read_from(slice.as_bytes()).ok_or(err_u32!(ManifestReadFailure))
+        if let Some(result) = ImageManifest::read_from(slice.as_bytes()) {
+            report_boot_status(FmcAliasManifestLoadComplete.into());
+            Ok(result)
+        } else {
+            raise_err!(ManifestReadFailure)
+        }
     }
 
     /// Verify the image
@@ -222,7 +235,7 @@ impl FmcAliasLayer {
             "[afmc] Image verified using Vendor ECC Key Index {}",
             info.vendor_ecc_pub_key_idx
         );
-
+        report_boot_status(FmcAliasImageVerificationComplete.into());
         Ok(info)
     }
 
@@ -264,6 +277,7 @@ impl FmcAliasLayer {
 
         txn.copy_request(runtime_dest)?;
 
+        report_boot_status(FmcAliasLoadImageComplete.into());
         Ok(())
     }
 
@@ -324,6 +338,8 @@ impl FmcAliasLayer {
 
         env.data_vault()
             .map(|d| d.write_warm_reset_entry4(WarmResetEntry4::ManifestAddr, slice));
+
+        report_boot_status(FmcAliasPopulateDataVaultComplete.into());
     }
 
     /// Derive Composite Device Identity (CDI) from FMC measurements
@@ -339,6 +355,7 @@ impl FmcAliasLayer {
         let data: [u8; 48] = measurements.into();
         let data = Hmac384Data::Slice(&data);
         Crypto::hmac384_mac(env, key, data, cdi)?;
+        report_boot_status(FmcAliasDeriveCdiComplete.into());
         Ok(())
     }
 
@@ -440,6 +457,7 @@ impl FmcAliasLayer {
         //  Copy TBS to DCCM.
         copy_tbs(tbs.tbs(), TbsType::FmcaliasTbs)?;
 
+        report_boot_status(FmcAliasCertSigGenerationComplete.into());
         Ok(())
     }
 
diff --git a/rom/dev/src/flow/cold_reset/idev_id.rs b/rom/dev/src/flow/cold_reset/idev_id.rs
index 450b6e81..8850c579 100644
--- a/rom/dev/src/flow/cold_reset/idev_id.rs
+++ b/rom/dev/src/flow/cold_reset/idev_id.rs
@@ -20,6 +20,7 @@ use crate::cprintln;
 use crate::flow::cold_reset::{KEY_ID_CDI, KEY_ID_FE, KEY_ID_IDEVID_PRIV_KEY, KEY_ID_UDS};
 use crate::print::HexBytes;
 use crate::rom_env::RomEnv;
+use caliptra_common::RomBootStatus::*;
 use caliptra_drivers::*;
 use caliptra_x509::*;
 
@@ -91,7 +92,10 @@ impl DiceLayer for InitDevIdLayer {
         // This information will be used by next DICE Layer while generating
         // certificates
         let subj_sn = X509::subj_sn(env, &key_pair.pub_key)?;
+        report_boot_status(IDevIdSubjIdSnGenerationComplete.into());
+
         let subj_key_id = X509::idev_subj_key_id(env, &key_pair.pub_key)?;
+        report_boot_status(IDevIdSubjKeyIdGenerationComplete.into());
 
         // Generate the output for next layer
         let output = DiceOutput {
@@ -104,6 +108,7 @@ impl DiceLayer for InitDevIdLayer {
         Self::generate_csr(env, &output)?;
 
         cprintln!("[idev] --");
+        report_boot_status(IDevIdDerivationComplete.into());
 
         // Return the DICE Layer Output
         Ok(output)
@@ -120,6 +125,7 @@ impl InitDevIdLayer {
     fn decrypt_uds(env: &RomEnv, uds: KeyId) -> CaliptraResult<()> {
         // Engage the Deobfuscation Engine to decrypt the UDS
         env.doe().map(|d| d.decrypt_uds(&DOE_UDS_IV, uds))?;
+        report_boot_status(IDevIdDecryptUdsComplete.into());
         Ok(())
     }
 
@@ -132,6 +138,7 @@ impl InitDevIdLayer {
     fn decrypt_field_entropy(env: &RomEnv, fe: KeyId) -> CaliptraResult<()> {
         // Engage the Deobfuscation Engine to decrypt the UDS
         env.doe().map(|d| d.decrypt_field_entropy(&DOE_FE_IV, fe))?;
+        report_boot_status(IDevIdDecryptFeComplete.into());
         Ok(())
     }
 
@@ -141,7 +148,11 @@ impl InitDevIdLayer {
     ///
     /// * `env` - ROM Environment
     fn clear_doe_secrets(env: &RomEnv) -> CaliptraResult<()> {
-        env.doe().map(|d| d.clear_secrets())
+        let result = env.doe().map(|d| d.clear_secrets());
+        if result.is_ok() {
+            report_boot_status(IDevIdClearDoeSecretsComplete.into());
+        }
+        result
     }
 
     /// Derive Composite Device Identity (CDI) from Unique Device Secret (UDS)
@@ -159,6 +170,8 @@ impl InitDevIdLayer {
 
         cprintln!("[idev] Erasing UDS.KEYID = {}", uds as u8);
         env.key_vault().map(|k| k.erase_key(uds))?;
+
+        report_boot_status(IDevIdCdiDerivationComplete.into());
         Ok(())
     }
 
@@ -174,7 +187,11 @@ impl InitDevIdLayer {
     ///
     /// * `Ecc384KeyPair` - Derive DICE Layer Key Pair
     fn derive_key_pair(env: &RomEnv, cdi: KeyId, priv_key: KeyId) -> CaliptraResult<Ecc384KeyPair> {
-        Crypto::ecc384_key_gen(env, cdi, priv_key)
+        let result = Crypto::ecc384_key_gen(env, cdi, priv_key);
+        if result.is_ok() {
+            report_boot_status(IDevIdKeyPairDerivationComplete.into());
+        }
+        result
     }
 
     /// Generate Local Device ID CSR
@@ -259,6 +276,7 @@ impl InitDevIdLayer {
         }
 
         cprintln!("[idev] CSR = {}", HexBytes(&csr[..csr_len]));
+        report_boot_status(IDevIdMakeCsrComplete.into());
 
         // Execute Send CSR Flow
         Self::send_csr(env, InitDevIdCsr::new(&csr, csr_len))
@@ -287,6 +305,7 @@ impl InitDevIdLayer {
                 txn.complete()?;
 
                 cprintln!("[idev] CSR uploaded");
+                report_boot_status(IDevIdSendCsrComplete.into());
 
                 // exit the loop
                 break Ok(());
diff --git a/rom/dev/src/flow/cold_reset/ldev_id.rs b/rom/dev/src/flow/cold_reset/ldev_id.rs
index 734f1b25..a20b8769 100644
--- a/rom/dev/src/flow/cold_reset/ldev_id.rs
+++ b/rom/dev/src/flow/cold_reset/ldev_id.rs
@@ -21,6 +21,7 @@ use crate::flow::cold_reset::{copy_tbs, TbsType};
 use crate::flow::cold_reset::{KEY_ID_CDI, KEY_ID_FE, KEY_ID_LDEVID_PRIV_KEY};
 use crate::print::HexBytes;
 use crate::rom_env::RomEnv;
+use caliptra_common::RomBootStatus::*;
 use caliptra_drivers::*;
 use caliptra_error::caliptra_err_def;
 use caliptra_x509::*;
@@ -72,7 +73,10 @@ impl DiceLayer for LocalDevIdLayer {
         // This information will be used by the next DICE Layer while generating
         // certificates
         let subj_sn = X509::subj_sn(env, &key_pair.pub_key)?;
+        report_boot_status(LDevIdSubjIdSnGenerationComplete.into());
+
         let subj_key_id = X509::subj_key_id(env, &key_pair.pub_key)?;
+        report_boot_status(LDevIdSubjKeyIdGenerationComplete.into());
 
         // Generate the output for next layer
         let output = DiceOutput {
@@ -85,6 +89,7 @@ impl DiceLayer for LocalDevIdLayer {
         Self::generate_cert_sig(env, input, &output)?;
 
         cprintln!("[ldev] --");
+        report_boot_status(LDevIdDerivationComplete.into());
 
         Ok(output)
     }
@@ -106,6 +111,7 @@ impl LocalDevIdLayer {
 
         cprintln!("[ldev] Erasing FE.KEYID = {}", fe as u8);
         env.key_vault().map(|k| k.erase_key(fe))?;
+        report_boot_status(LDevIdCdiDerivationComplete.into());
         Ok(())
     }
 
@@ -121,7 +127,11 @@ impl LocalDevIdLayer {
     ///
     /// * `Ecc384KeyPair` - Derive DICE Layer Key Pair
     fn derive_key_pair(env: &RomEnv, cdi: KeyId, priv_key: KeyId) -> CaliptraResult<Ecc384KeyPair> {
-        Crypto::ecc384_key_gen(env, cdi, priv_key)
+        let result = Crypto::ecc384_key_gen(env, cdi, priv_key);
+        if result.is_ok() {
+            report_boot_status(LDevIdKeyPairDerivationComplete.into());
+        }
+        result
     }
 
     /// Generate Local Device ID Certificate Signature
@@ -198,6 +208,7 @@ impl LocalDevIdLayer {
         //  Copy TBS to DCCM.
         copy_tbs(tbs.tbs(), TbsType::LdevidTbs)?;
 
+        report_boot_status(LDevIdCertSigGenerationComplete.into());
         Ok(())
     }
 }
diff --git a/rom/dev/src/pcr.rs b/rom/dev/src/pcr.rs
index 9ee26596..6dd9ea15 100644
--- a/rom/dev/src/pcr.rs
+++ b/rom/dev/src/pcr.rs
@@ -59,6 +59,5 @@ pub fn extend_pcr0(env: &RomEnv) -> CaliptraResult<()> {
     extend_u8(env.data_vault().map(|d| d.fmc_svn()) as u8)?;
 
     // TODO: Check PCR0 != 0
-
     Ok(())
 }


Test patch:
diff --git a/rom/dev/tests/test_dice_derivations.rs b/rom/dev/tests/test_dice_derivations.rs
new file mode 100644
index 00000000..7f6c1b74
--- /dev/null
+++ b/rom/dev/tests/test_dice_derivations.rs
@@ -0,0 +1,87 @@
+// Licensed under the Apache-2.0 license
+
+use caliptra_builder::ImageOptions;
+use caliptra_common::RomBootStatus;
+use caliptra_common::RomBootStatus::*;
+use caliptra_hw_model::DefaultHwModel;
+use caliptra_hw_model::Fuses;
+use caliptra_hw_model::HwModel;
+
+pub mod helpers;
+
+#[track_caller]
+fn step_until_boot_status(hw: &mut DefaultHwModel, expected_status: RomBootStatus) {
+    // Since the boot takes less than 20M cycles, we know something is wrong if
+    // we're stuck at the same state for that duration.
+    const MAX_WAIT_CYCLES: u32 = 20_000_000;
+
+    let mut cycle_count = 0u32;
+    let expected_status_u32: u32 = expected_status.into();
+    let initial_boot_status_u32 = hw.soc_ifc().cptra_boot_status().read();
+    loop {
+        let actual_status_u32 = hw.soc_ifc().cptra_boot_status().read();
+        if expected_status_u32 == actual_status_u32 {
+            break;
+        }
+        if actual_status_u32 != initial_boot_status_u32 {
+            panic!(
+                "Expected the next boot_status to be {expected_status:?} \
+                    ({expected_status_u32}), but status changed from \
+                    {initial_boot_status_u32} to {actual_status_u32})"
+            );
+        }
+        hw.step();
+        cycle_count += 1;
+        if cycle_count >= MAX_WAIT_CYCLES {
+            panic!(
+                "Expected boot_status to be {expected_status:?} \
+                    ({expected_status_u32}), but was stuck at ({actual_status_u32})"
+            );
+        }
+    }
+}
+
+#[test]
+fn test_status_reporting() {
+    let (mut hw, image_bundle) =
+        helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
+
+    step_until_boot_status(&mut hw, IDevIdDecryptUdsComplete);
+    step_until_boot_status(&mut hw, IDevIdDecryptFeComplete);
+    step_until_boot_status(&mut hw, IDevIdClearDoeSecretsComplete);
+    step_until_boot_status(&mut hw, IDevIdCdiDerivationComplete);
+    step_until_boot_status(&mut hw, IDevIdKeyPairDerivationComplete);
+    step_until_boot_status(&mut hw, IDevIdSubjIdSnGenerationComplete);
+    step_until_boot_status(&mut hw, IDevIdSubjKeyIdGenerationComplete);
+    // step_until_boot_status(IdevIdMakeCsrComplete);
+    // step_until_boot_status(IdevIdSendCsrComplete);
+    step_until_boot_status(&mut hw, IDevIdDerivationComplete);
+    step_until_boot_status(&mut hw, LDevIdCdiDerivationComplete);
+    step_until_boot_status(&mut hw, LDevIdKeyPairDerivationComplete);
+    step_until_boot_status(&mut hw, LDevIdSubjIdSnGenerationComplete);
+    step_until_boot_status(&mut hw, LDevIdSubjKeyIdGenerationComplete);
+    step_until_boot_status(&mut hw, LDevIdCertSigGenerationComplete);
+    step_until_boot_status(&mut hw, LDevIdDerivationComplete);
+
+    // Wait for uploading firmware.
+    hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw());
+
+    assert!(hw
+        .upload_firmware(&image_bundle.to_bytes().unwrap())
+        .is_ok());
+
+    // [TODO] Don't use upload_firmware (whic returns only when the txn is complete); manually upload the firmware to test these boot statuses.
+    // step_until_boot_status(FmcAliasDownloadImageComplete);
+    // step_until_boot_status(FmcAliasManifestLoadComplete);
+    // step_until_boot_status(FmcAliasImageVerificationComplete);
+    // step_until_boot_status(FmcAliasPopulateDataVaultComplete);
+    // step_until_boot_status(FmcAliasExtendPcrComplete);
+    // step_until_boot_status(FmcAliasLoadImageComplete);
+    step_until_boot_status(&mut hw, FmcAliasFirmwareDownloadTxComplete);
+    step_until_boot_status(&mut hw, FmcAliasDeriveCdiComplete);
+    step_until_boot_status(&mut hw, FmcAliasKeyPairDerivationComplete);
+    step_until_boot_status(&mut hw, FmcAliasSubjIdSnGenerationComplete);
+    step_until_boot_status(&mut hw, FmcAliasSubjKeyIdGenerationComplete);
+    step_until_boot_status(&mut hw, FmcAliasCertSigGenerationComplete);
+    step_until_boot_status(&mut hw, FmcAliasDerivationComplete);
+}