Repository: chipsalliance/caliptra-sw
Pull request #1182 (2023-12-09 02:32:52)

Diversify Runtime error codes
Also, in places where we disable attestation, signal a non fatal error to SoC so there is some way to find out what went wrong, other than looking at UART.

After this, we still need to find a way to pass DPE error code info up to runtime, instead of throwing it away by using map_err.


Patch:
diff --git a/error/src/lib.rs b/error/src/lib.rs
index b9566248..a984df65 100644
--- a/error/src/lib.rs
+++ b/error/src/lib.rs
@@ -369,10 +369,36 @@ impl CaliptraError {
         CaliptraError::new_const(0x000E0027);
     pub const RUNTIME_LDEVID_CERT_HANDOFF_FAILED: CaliptraError =
         CaliptraError::new_const(0x000E0028);
-    pub const RUNTIME_CONTEXT_TAG_VALIDATION_FAILED: CaliptraError =
+    pub const RUNTIME_CONTEXT_TAGS_VALIDATION_FAILED: CaliptraError =
         CaliptraError::new_const(0x000E0029);
-    pub const RUNTIME_INCREMENT_PCR_RESET_MAX_REACHED: CaliptraError =
+    pub const RUNTIME_COULD_NOT_GET_DPE_PROFILE: CaliptraError =
         CaliptraError::new_const(0x000E002A);
+    pub const RUNTIME_DPE_COMMAND_DESERIALIZATION_FAILED: CaliptraError =
+        CaliptraError::new_const(0x000E002B);
+    pub const RUNTIME_GET_LDEVID_CERT_FAILED: CaliptraError = CaliptraError::new_const(0x000E002C);
+    pub const RUNTIME_GET_FMC_ALIAS_CERT_FAILED: CaliptraError =
+        CaliptraError::new_const(0x000E002D);
+    pub const RUNTIME_GET_RT_ALIAS_CERT_FAILED: CaliptraError =
+        CaliptraError::new_const(0x000E002E);
+    pub const RUNTIME_CMD_BUSY_DURING_WARM_RESET: CaliptraError =
+        CaliptraError::new_const(0x000E002F);
+    pub const RUNTIME_RT_SVN_HANDOFF_FAILED: CaliptraError = CaliptraError::new_const(0x000E0030);
+    pub const RUNTIME_RT_MIN_SVN_HANDOFF_FAILED: CaliptraError =
+        CaliptraError::new_const(0x000E0031);
+    pub const RUNTIME_FMC_SVN_HANDOFF_FAILED: CaliptraError = CaliptraError::new_const(0x000E0032);
+    pub const RUNTIME_CONTEXT_HAS_TAG_VALIDATION_FAILED: CaliptraError =
+        CaliptraError::new_const(0x000E0033);
+    pub const RUNTIME_LDEV_ID_CERT_TOO_BIG: CaliptraError = CaliptraError::new_const(0x000E0034);
+    pub const RUNTIME_FMC_ALIAS_CERT_TOO_BIG: CaliptraError = CaliptraError::new_const(0x000E0035);
+    pub const RUNTIME_RT_ALIAS_CERT_TOO_BIG: CaliptraError = CaliptraError::new_const(0x000E0036);
+    pub const RUNTIME_COMPUTE_RT_ALIAS_SN_FAILED: CaliptraError =
+        CaliptraError::new_const(0x000E0037);
+    pub const RUNTIME_RT_JOURNEY_PCR_VALIDATION_FAILED: CaliptraError =
+        CaliptraError::new_const(0x000E0038);
+    pub const RUNTIME_UNABLE_TO_FIND_DPE_ROOT_CONTEXT: CaliptraError =
+        CaliptraError::new_const(0x000E0039);
+    pub const RUNTIME_INCREMENT_PCR_RESET_MAX_REACHED: CaliptraError =
+        CaliptraError::new_const(0x000E003A);
 
     /// FMC Errors
     pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
diff --git a/runtime/src/dice.rs b/runtime/src/dice.rs
index 7d8ee544..d1d75ced 100644
--- a/runtime/src/dice.rs
+++ b/runtime/src/dice.rs
@@ -155,6 +155,7 @@ pub fn copy_ldevid_cert(
         .get(..persistent_data.fht.ldevid_tbs_size.into());
     let sig = ldevid_dice_sign(persistent_data, dv)?;
     cert_from_tbs_and_sig(tbs, &sig, cert)
+        .map_err(|_| CaliptraError::RUNTIME_GET_LDEVID_CERT_FAILED)
 }
 
 // Retrieve the r portion of the FMC cert signature
@@ -220,6 +221,7 @@ pub fn copy_fmc_alias_cert(
         .get(..persistent_data.fht.fmcalias_tbs_size.into());
     let sig = fmc_dice_sign(persistent_data, dv)?;
     cert_from_tbs_and_sig(tbs, &sig, cert)
+        .map_err(|_| CaliptraError::RUNTIME_GET_FMC_ALIAS_CERT_FAILED)
 }
 
 /// Copy RT Alias certificate produced by ROM to `cert` buffer
@@ -234,6 +236,7 @@ pub fn copy_rt_alias_cert(
         .rtalias_tbs
         .get(..persistent_data.fht.rtalias_tbs_size.into());
     cert_from_tbs_and_sig(tbs, &persistent_data.fht.rt_dice_sign, cert)
+        .map_err(|_| CaliptraError::RUNTIME_GET_RT_ALIAS_CERT_FAILED)
 }
 
 /// Create a certificate from a tbs and a signature and write the output to `cert`
@@ -243,7 +246,7 @@ fn cert_from_tbs_and_sig(
     cert: &mut [u8],
 ) -> CaliptraResult<usize> {
     let Some(tbs) = tbs else {
-        return Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY);
+        return Err(CaliptraError::RUNTIME_INTERNAL);
     };
 
     // Convert from Ecc384Signature to Ecdsa384Signature
@@ -252,11 +255,11 @@ fn cert_from_tbs_and_sig(
         s: sig.s.into(),
     };
     let Some(builder) = Ecdsa384CertBuilder::new(tbs, &bldr_sig) else {
-        return Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY);
+        return Err(CaliptraError::RUNTIME_INTERNAL);
     };
 
     let Some(size) = builder.build(cert) else {
-        return Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY);
+        return Err(CaliptraError::RUNTIME_INTERNAL);
     };
 
     Ok(size)
diff --git a/runtime/src/drivers.rs b/runtime/src/drivers.rs
index 94e5cdd8..c9660ac2 100644
--- a/runtime/src/drivers.rs
+++ b/runtime/src/drivers.rs
@@ -157,10 +157,10 @@ impl Drivers {
             .find(|&(idx, context)| {
                 context.state != ContextState::Inactive && context.parent_idx == Context::ROOT_INDEX
             })
-            .ok_or(CaliptraError::RUNTIME_DPE_VALIDATION_FAILED)?
+            .ok_or(CaliptraError::RUNTIME_UNABLE_TO_FIND_DPE_ROOT_CONTEXT)?
             .0;
         if root_idx >= dpe.contexts.len() {
-            return Err(CaliptraError::RUNTIME_DPE_VALIDATION_FAILED);
+            return Err(CaliptraError::RUNTIME_UNABLE_TO_FIND_DPE_ROOT_CONTEXT);
         }
         Ok(root_idx)
     }
@@ -173,7 +173,12 @@ impl Drivers {
             // If SRAM Dpe Instance validation fails, disable attestation
             let mut result = DisableAttestationCmd::execute(drivers);
             match result {
-                Ok(_) => cprintln!("Disabled attestation due to DPE validation failure"),
+                Ok(_) => {
+                    cprintln!("Disabled attestation due to DPE validation failure");
+                    caliptra_drivers::report_fw_error_non_fatal(
+                        CaliptraError::RUNTIME_DPE_VALIDATION_FAILED.into(),
+                    );
+                }
                 Err(e) => {
                     cprintln!("{}", e.0);
                     return Err(CaliptraError::RUNTIME_GLOBAL_EXCEPTION);
@@ -202,19 +207,19 @@ impl Drivers {
         let mut hasher = drivers
             .sha384
             .digest_init()
-            .map_err(|_| CaliptraError::RUNTIME_DPE_VALIDATION_FAILED)?;
+            .map_err(|_| CaliptraError::RUNTIME_RT_JOURNEY_PCR_VALIDATION_FAILED)?;
 
         hasher
             .update(&[0; AlgLen::Bit384.size()])
-            .map_err(|_| CaliptraError::RUNTIME_DPE_VALIDATION_FAILED)?;
+            .map_err(|_| CaliptraError::RUNTIME_RT_JOURNEY_PCR_VALIDATION_FAILED)?;
         hasher
             .update(&latest_tci.0)
-            .map_err(|_| CaliptraError::RUNTIME_DPE_VALIDATION_FAILED)?;
+            .map_err(|_| CaliptraError::RUNTIME_RT_JOURNEY_PCR_VALIDATION_FAILED)?;
 
         let mut digest = Array4x12::default();
         hasher
             .finalize(&mut digest)
-            .map_err(|_| CaliptraError::RUNTIME_DPE_VALIDATION_FAILED)?;
+            .map_err(|_| CaliptraError::RUNTIME_RT_JOURNEY_PCR_VALIDATION_FAILED)?;
 
         let latest_pcr = drivers.pcr_bank.read_pcr(RT_FW_JOURNEY_PCR);
         // Ensure SHA384_HASH(0x00..00, TCI from SRAM) == RT_FW_JOURNEY_PCR
@@ -222,7 +227,12 @@ impl Drivers {
             // If latest pcr validation fails, disable attestation
             let mut result = DisableAttestationCmd::execute(drivers);
             match result {
-                Ok(_) => cprintln!("Disabled attestation due to latest TCI of the node containing the runtime journey PCR not matching the runtime PCR"),
+                Ok(_) => {
+                    cprintln!("Disabled attestation due to latest TCI of the node containing the runtime journey PCR not matching the runtime PCR");
+                    caliptra_drivers::report_fw_error_non_fatal(
+                        CaliptraError::RUNTIME_RT_JOURNEY_PCR_VALIDATION_FAILED.into(),
+                    );
+                }
                 Err(e) => {
                     cprintln!("{}", e.0);
                     return Err(CaliptraError::RUNTIME_GLOBAL_EXCEPTION);
@@ -240,10 +250,12 @@ impl Drivers {
         let dpe = &pdata.dpe;
 
         for i in (0..MAX_HANDLES) {
-            if dpe.contexts[i].state == ContextState::Inactive
-                && (context_has_tag[i].get() || context_tags[i] != 0)
-            {
-                return Err(CaliptraError::RUNTIME_CONTEXT_TAG_VALIDATION_FAILED);
+            if dpe.contexts[i].state == ContextState::Inactive {
+                if context_tags[i] != 0 {
+                    return Err(CaliptraError::RUNTIME_CONTEXT_TAGS_VALIDATION_FAILED);
+                } else if context_has_tag[i].get() {
+                    return Err(CaliptraError::RUNTIME_CONTEXT_HAS_TAG_VALIDATION_FAILED);
+                }
             }
         }
         Ok(())
@@ -255,7 +267,7 @@ impl Drivers {
 
         let rt_digest = self.sha256.digest(&key)?;
         let token = CryptoBuf::new(&Into::<[u8; 32]>::into(rt_digest))
-            .map_err(|_| CaliptraError::RUNTIME_INITIALIZE_DPE_FAILED)?;
+            .map_err(|_| CaliptraError::RUNTIME_COMPUTE_RT_ALIAS_SN_FAILED)?;
 
         Ok(token)
     }
@@ -369,10 +381,10 @@ impl Drivers {
         let mut cert = [0u8; MAX_CERT_CHAIN_SIZE];
 
         // Write ldev_id cert to cert chain.
-        let ldevid_cert_size = dice::copy_ldevid_cert(data_vault, persistent_data.get(), &mut cert)
-            .map_err(|_| CaliptraError::RUNTIME_CERT_CHAIN_CREATION_FAILED)?;
+        let ldevid_cert_size =
+            dice::copy_ldevid_cert(data_vault, persistent_data.get(), &mut cert)?;
         if ldevid_cert_size > cert.len() {
-            return Err(CaliptraError::RUNTIME_CERT_CHAIN_CREATION_FAILED);
+            return Err(CaliptraError::RUNTIME_LDEV_ID_CERT_TOO_BIG);
         }
 
         // Write fmc alias cert to cert chain.
@@ -380,21 +392,19 @@ impl Drivers {
             data_vault,
             persistent_data.get(),
             &mut cert[ldevid_cert_size..],
-        )
-        .map_err(|_| CaliptraError::RUNTIME_CERT_CHAIN_CREATION_FAILED)?;
+        )?;
         if ldevid_cert_size + fmcalias_cert_size > cert.len() {
-            return Err(CaliptraError::RUNTIME_CERT_CHAIN_CREATION_FAILED);
+            return Err(CaliptraError::RUNTIME_FMC_ALIAS_CERT_TOO_BIG);
         }
 
         // Write rt alias cert to cert chain.
         let rtalias_cert_size = dice::copy_rt_alias_cert(
             persistent_data.get(),
             &mut cert[ldevid_cert_size + fmcalias_cert_size..],
-        )
-        .map_err(|_| CaliptraError::RUNTIME_CERT_CHAIN_CREATION_FAILED)?;
+        )?;
         let cert_chain_size = ldevid_cert_size + fmcalias_cert_size + rtalias_cert_size;
         if cert_chain_size > cert.len() {
-            return Err(CaliptraError::RUNTIME_CERT_CHAIN_CREATION_FAILED);
+            return Err(CaliptraError::RUNTIME_RT_ALIAS_CERT_TOO_BIG);
         }
 
         // Copy cert chain to ArrayVec.
diff --git a/runtime/src/handoff.rs b/runtime/src/handoff.rs
index d00dfcbb..06ac8fb6 100644
--- a/runtime/src/handoff.rs
+++ b/runtime/src/handoff.rs
@@ -14,22 +14,25 @@ impl RtHandoff<'_> {
         match ds {
             DataVaultNonSticky4(dv_entry) => Ok(self.data_vault.read_warm_reset_entry4(dv_entry)),
             DataVaultSticky4(dv_entry) => Ok(self.data_vault.read_cold_reset_entry4(dv_entry)),
-            _ => Err(CaliptraError::RUNTIME_HANDOFF_INVALID_PARM),
+            _ => Err(CaliptraError::RUNTIME_INTERNAL),
         }
     }
 
     /// Retrieve runtime SVN.
     pub fn rt_svn(&self) -> CaliptraResult<u32> {
         self.read_from_ds(self.fht.rt_svn_dv_hdl.try_into()?)
+            .map_err(|_| CaliptraError::RUNTIME_RT_SVN_HANDOFF_FAILED)
     }
 
     /// Retrieve runtime minimum SVN.
     pub fn rt_min_svn(&self) -> CaliptraResult<u32> {
         self.read_from_ds(self.fht.rt_min_svn_dv_hdl.try_into()?)
+            .map_err(|_| CaliptraError::RUNTIME_RT_MIN_SVN_HANDOFF_FAILED)
     }
 
     /// Retrieve FMC SVN.
     pub fn fmc_svn(&self) -> CaliptraResult<u32> {
         self.read_from_ds(self.fht.fmc_svn_dv_hdl.try_into()?)
+            .map_err(|_| CaliptraError::RUNTIME_FMC_SVN_HANDOFF_FAILED)
     }
 }
diff --git a/runtime/src/invoke_dpe.rs b/runtime/src/invoke_dpe.rs
index 5418a295..d9b8a057 100644
--- a/runtime/src/invoke_dpe.rs
+++ b/runtime/src/invoke_dpe.rs
@@ -49,7 +49,7 @@ impl InvokeDpeCmd {
 
             let locality = drivers.mbox.user();
             let command = Command::deserialize(&cmd.data[..cmd.data_size as usize])
-                .map_err(|_| CaliptraError::RUNTIME_INVOKE_DPE_FAILED)?;
+                .map_err(|_| CaliptraError::RUNTIME_DPE_COMMAND_DESERIALIZATION_FAILED)?;
             let flags = pdata.manifest1.header.flags;
 
             let pdata_mut = drivers.persistent_data.get_mut();
@@ -59,7 +59,7 @@ impl InvokeDpeCmd {
             let resp = match command {
                 Command::GetProfile => Ok(Response::GetProfile(
                     dpe.get_profile(&mut env.platform)
-                        .map_err(|_| CaliptraError::RUNTIME_INVOKE_DPE_FAILED)?,
+                        .map_err(|_| CaliptraError::RUNTIME_COULD_NOT_GET_DPE_PROFILE)?,
                 )),
                 Command::InitCtx(cmd) => {
                     // InitCtx can only create new contexts if they are simulation contexts.
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 22e79b5d..afedc3ea 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -195,7 +195,12 @@ pub fn handle_mailbox_commands(drivers: &mut Drivers) -> CaliptraResult<()> {
         if reset_reason == ResetReason::WarmReset {
             let mut result = DisableAttestationCmd::execute(drivers);
             match result {
-                Ok(_) => cprintln!("Disabled attestation due to cmd busy during warm reset"),
+                Ok(_) => {
+                    cprintln!("Disabled attestation due to cmd busy during warm reset");
+                    caliptra_drivers::report_fw_error_non_fatal(
+                        CaliptraError::RUNTIME_CMD_BUSY_DURING_WARM_RESET.into(),
+                    );
+                }
                 Err(e) => {
                     cprintln!("{}", e.0);
                     return Err(CaliptraError::RUNTIME_GLOBAL_EXCEPTION);


Test patch:
diff --git a/runtime/tests/runtime_integration_tests/test_update_reset.rs b/runtime/tests/runtime_integration_tests/test_update_reset.rs
index 58b95897..36378549 100644
--- a/runtime/tests/runtime_integration_tests/test_update_reset.rs
+++ b/runtime/tests/runtime_integration_tests/test_update_reset.rs
@@ -135,7 +135,7 @@ fn test_context_tags_validation() {
 
     model.step_until(|m| {
         m.soc_ifc().cptra_fw_error_non_fatal().read()
-            == u32::from(CaliptraError::RUNTIME_CONTEXT_TAG_VALIDATION_FAILED)
+            == u32::from(CaliptraError::RUNTIME_CONTEXT_TAGS_VALIDATION_FAILED)
     });
 }
 
@@ -167,7 +167,7 @@ fn test_context_has_tag_validation() {
 
     model.step_until(|m| {
         m.soc_ifc().cptra_fw_error_non_fatal().read()
-            == u32::from(CaliptraError::RUNTIME_CONTEXT_TAG_VALIDATION_FAILED)
+            == u32::from(CaliptraError::RUNTIME_CONTEXT_HAS_TAG_VALIDATION_FAILED)
     });
 }
 
@@ -198,6 +198,10 @@ fn test_dpe_validation() {
     model
         .mailbox_execute(u32::from(CommandId::FIRMWARE_LOAD), &updated_fw_image)
         .unwrap();
+    model.step_until(|m| {
+        m.soc_ifc().cptra_fw_error_non_fatal().read()
+            == u32::from(CaliptraError::RUNTIME_DPE_VALIDATION_FAILED)
+    });
 
     // check attestation disabled via FW_INFO
     let payload = MailboxReqHeader {