Repository: chipsalliance/caliptra-sw
Pull request #444 (2023-06-29 20:13:32)

Add impactless update command to RT
1. Add impactless update mailbox command
2. Fix a keyvault emulation bug which was clearing usage bits
3. Make ROM use the old FHT on update reset


Patch:
diff --git a/drivers/src/mailbox.rs b/drivers/src/mailbox.rs
index c6146b2f..baa5aad6 100644
--- a/drivers/src/mailbox.rs
+++ b/drivers/src/mailbox.rs
@@ -347,6 +347,19 @@ impl MailboxRecvTxn<'_> {
         Ok(())
     }
 
+    /// Pulls at most `count` words from the mailbox and throws them away
+    pub fn drop_words(&mut self, count: usize) -> CaliptraResult<()> {
+        let mbox = self.mbox.regs_mut();
+        let dlen_bytes = mbox.dlen().read() as usize;
+        let dlen_words = (dlen_bytes + 3) / 4;
+        let words_to_read = min(count, dlen_words);
+        for _ in 0..words_to_read {
+            _ = mbox.dataout().read();
+        }
+
+        Ok(())
+    }
+
     /// Pulls at most `data.len()` words from the mailbox FIFO without performing state transition.
     ///
     /// # Arguments
@@ -356,7 +369,7 @@ impl MailboxRecvTxn<'_> {
     /// # Returns
     ///
     /// Status of Operation
-    ///   
+    ///
     pub fn copy_request(&mut self, data: &mut [u32]) -> CaliptraResult<()> {
         if self.state != MailboxOpState::Execute {
             return Err(CaliptraError::DRIVER_MAILBOX_INVALID_STATE);
@@ -374,7 +387,7 @@ impl MailboxRecvTxn<'_> {
     /// # Returns
     ///
     /// Status of Operation
-    ///   
+    ///
     pub fn recv_request(&mut self, data: &mut [u32]) -> CaliptraResult<()> {
         self.copy_request(data)?;
         self.complete(true)?;
diff --git a/error/src/lib.rs b/error/src/lib.rs
index 2b5bb912..15c0f195 100644
--- a/error/src/lib.rs
+++ b/error/src/lib.rs
@@ -109,7 +109,7 @@ impl CaliptraError {
     pub const DRIVER_MAILBOX_INVALID_DATA_LEN: CaliptraError = CaliptraError::new_const(0x00080002);
     pub const DRIVER_MAILBOX_ENQUEUE_ERR: CaliptraError = CaliptraError::new_const(0x00080004);
 
-    /// SHA384ACC Errors.   
+    /// SHA384ACC Errors.
     pub const DRIVER_SHA384ACC_INDEX_OUT_OF_BOUNDS: CaliptraError =
         CaliptraError::new_const(0x00090003);
     /// SHA1 Errors.
@@ -268,6 +268,8 @@ impl CaliptraError {
     pub const RUNTIME_ECDSA_VERIFY_FAILED: CaliptraError = CaliptraError::new_const(0x000E0004);
     pub const RUNTIME_INVALID_CHECKSUM: CaliptraError = CaliptraError::new_const(0x000E0005);
     pub const RUNTIME_FIPS_UNIMPLEMENTED: CaliptraError = CaliptraError::new_const(0x000E0006);
+    pub const RUNTIME_UNEXPECTED_UPDATE_RETURN: CaliptraError =
+        CaliptraError::new_const(0x000E0007);
 
     /// FMC Errors
     pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
@@ -317,6 +319,8 @@ impl CaliptraError {
         CaliptraError::new_const(0x01040003);
     pub const ROM_UPDATE_RESET_FLOW_MAILBOX_ACCESS_FAILURE: CaliptraError =
         CaliptraError::new_const(0x01040004);
+    pub const ROM_UPDATE_RESET_READ_FHT_FAILURE: CaliptraError =
+        CaliptraError::new_const(0x01040005);
 
     /// Unknown Reset Error
     pub const ROM_UNKNOWN_RESET_FLOW: CaliptraError = CaliptraError::new_const(0x01040020);
diff --git a/rom/dev/src/flow/cold_reset/mod.rs b/rom/dev/src/flow/cold_reset/mod.rs
index 37bd0062..d6d4453e 100644
--- a/rom/dev/src/flow/cold_reset/mod.rs
+++ b/rom/dev/src/flow/cold_reset/mod.rs
@@ -48,7 +48,7 @@ impl ColdResetFlow {
     ///
     /// * `env` - ROM Environment
     #[inline(never)]
-    pub fn run(env: &mut RomEnv) -> CaliptraResult<FirmwareHandoffTable> {
+    pub fn run(env: &mut RomEnv) -> CaliptraResult<Option<FirmwareHandoffTable>> {
         cprintln!("[cold-reset] ++");
         report_boot_status(ColdResetStarted.into());
 
@@ -73,7 +73,7 @@ impl ColdResetFlow {
         cprintln!("[cold-reset] --");
         report_boot_status(ColdResetComplete.into());
 
-        Ok(fht::make_fht(env))
+        Ok(Some(fht::make_fht(env)))
     }
 }
 
diff --git a/rom/dev/src/flow/mod.rs b/rom/dev/src/flow/mod.rs
index 7825240c..0efd7001 100644
--- a/rom/dev/src/flow/mod.rs
+++ b/rom/dev/src/flow/mod.rs
@@ -26,7 +26,7 @@ use caliptra_error::CaliptraError;
 /// # Arguments
 ///
 /// * `env` - ROM Environment
-pub fn run(env: &mut RomEnv) -> CaliptraResult<FirmwareHandoffTable> {
+pub fn run(env: &mut RomEnv) -> CaliptraResult<Option<FirmwareHandoffTable>> {
     let reset_reason = env.soc_ifc.reset_reason();
     match reset_reason {
         // Cold Reset Flow
diff --git a/rom/dev/src/flow/update_reset.rs b/rom/dev/src/flow/update_reset.rs
index 5fc2c63e..f7f71c6c 100644
--- a/rom/dev/src/flow/update_reset.rs
+++ b/rom/dev/src/flow/update_reset.rs
@@ -11,7 +11,7 @@ Abstract:
     File contains the implementation of update reset flow.
 
 --*/
-use crate::{cprintln, fht, rom_env::RomEnv, verifier::RomImageVerificationEnv, wdt};
+use crate::{cprintln, rom_env::RomEnv, verifier::RomImageVerificationEnv, wdt};
 
 use caliptra_common::memory_layout::{MAN1_ORG, MAN2_ORG};
 use caliptra_common::FirmwareHandoffTable;
@@ -34,7 +34,7 @@ impl UpdateResetFlow {
     /// # Arguments
     ///
     /// * `env` - ROM Environment
-    pub fn run(env: &mut RomEnv) -> CaliptraResult<FirmwareHandoffTable> {
+    pub fn run(env: &mut RomEnv) -> CaliptraResult<Option<FirmwareHandoffTable>> {
         cprintln!("[update-reset] ++");
         report_boot_status(UpdateResetStarted.into());
 
@@ -83,7 +83,8 @@ impl UpdateResetFlow {
 
         cprintln!("[update-reset Success] --");
         report_boot_status(UpdateResetComplete.into());
-        Ok(fht::make_fht(env))
+
+        Ok(None)
     }
 
     /// Verify the image
@@ -122,7 +123,6 @@ impl UpdateResetFlow {
 
         let src = unsafe {
             let ptr = MAN2_ORG as *mut u32;
-
             core::slice::from_raw_parts_mut(ptr, core::mem::size_of::<ImageManifest>() / 4)
         };
 
@@ -143,6 +143,9 @@ impl UpdateResetFlow {
             manifest.runtime.size
         );
 
+        // Throw away the FMC portion of the image
+        txn.drop_words(manifest.fmc.size as usize / 4)?;
+
         let runtime_dest = unsafe {
             let addr = (manifest.runtime.load_addr) as *mut u32;
             core::slice::from_raw_parts_mut(addr, manifest.runtime.size as usize / 4)
diff --git a/rom/dev/src/flow/warm_reset.rs b/rom/dev/src/flow/warm_reset.rs
index 11792c98..2ddfa3f1 100644
--- a/rom/dev/src/flow/warm_reset.rs
+++ b/rom/dev/src/flow/warm_reset.rs
@@ -28,7 +28,7 @@ impl WarmResetFlow {
     ///
     /// * `env` - ROM Environment
     #[inline(never)]
-    pub fn run(env: &mut RomEnv) -> CaliptraResult<FirmwareHandoffTable> {
+    pub fn run(env: &mut RomEnv) -> CaliptraResult<Option<FirmwareHandoffTable>> {
         cprintln!("[warm-reset] ++");
 
         // [TODO] Remove this when RTL bug is fixed.
@@ -49,7 +49,7 @@ impl WarmResetFlow {
 
         cprintln!("[warm-reset] --");
 
-        Ok(fht)
+        Ok(None)
     }
 
     fn load_manifest(manifest_load_addr: u32) -> CaliptraResult<ImageManifest> {
diff --git a/rom/dev/src/main.rs b/rom/dev/src/main.rs
index 6b9ca33d..78cead5e 100644
--- a/rom/dev/src/main.rs
+++ b/rom/dev/src/main.rs
@@ -86,10 +86,10 @@ pub extern "C" fn rom_entry() -> ! {
 
     let result = flow::run(&mut env);
     match result {
-        Ok(fht) => {
+        Ok(Some(fht)) => {
             fht::store(fht);
         }
-
+        Ok(None) => {}
         Err(err) => {
             //
             // For the update reset case, when we fail the image validation
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 655f033a..fbd595b5 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -3,6 +3,7 @@
 #![no_std]
 
 pub mod dice;
+mod update;
 mod verify;
 
 // Used by runtime tests
@@ -22,6 +23,7 @@ use caliptra_registers::{
     ecc::EccReg,
     mbox::{enums::MboxStatusE, MboxCsr},
     sha512_acc::Sha512AccCsr,
+    soc_ifc::SocIfcReg,
 };
 use zerocopy::{AsBytes, FromBytes};
 
@@ -63,6 +65,7 @@ pub struct Drivers<'a> {
     pub sha_acc: Sha512AccCsr,
     pub ecdsa: Ecc384,
     pub data_vault: DataVault,
+    pub soc_ifc: SocIfcReg,
     pub fht: &'a mut FirmwareHandoffTable,
 }
 impl<'a> Drivers<'a> {
@@ -77,6 +80,7 @@ impl<'a> Drivers<'a> {
             sha_acc: Sha512AccCsr::new(),
             ecdsa: Ecc384::new(EccReg::new()),
             data_vault: DataVault::new(DvReg::new()),
+            soc_ifc: SocIfcReg::new(),
             fht,
         }
     }
@@ -112,7 +116,18 @@ fn wait_for_cmd(_mbox: &mut Mailbox) {
 }
 
 fn handle_command(drivers: &mut Drivers) -> CaliptraResult<MboxStatusE> {
+    // For firmware update, don't read data from the mailbox
+    if drivers.mbox.cmd() == CommandId::FIRMWARE_LOAD.into() {
+        update::handle_impactless_update(drivers)?;
+
+        // If the handler succeeds but does not invoke reset that is
+        // unexpected. Denote that the update failed.
+        return Err(CaliptraError::RUNTIME_UNEXPECTED_UPDATE_RETURN);
+    }
+
+    // Get the command bytes
     let packet = Packet::copy_from_mbox(drivers)?;
+    let cmd_bytes = packet.as_bytes()?;
 
     cprintln!(
         "[rt] Received command=0x{:x}, len={}",
@@ -120,11 +135,9 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult<MboxStatusE> {
         packet.len
     );
 
-    // Get the command bytes
-    let cmd_bytes = packet.as_bytes()?;
-
     match CommandId::from(packet.cmd) {
-        CommandId::FIRMWARE_LOAD => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND),
+        // FIRMWARE_LOAD expected to already be handled
+        CommandId::FIRMWARE_LOAD => Err(CaliptraError::RUNTIME_UNEXPECTED_UPDATE_RETURN),
         CommandId::GET_IDEV_CSR => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND),
         CommandId::GET_LDEV_CERT => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND),
         CommandId::ECDSA384_VERIFY => {
diff --git a/runtime/src/update.rs b/runtime/src/update.rs
new file mode 100644
index 00000000..f380cadb
--- /dev/null
+++ b/runtime/src/update.rs
@@ -0,0 +1,22 @@
+// Licensed under the Apache-2.0 license
+
+use crate::Drivers;
+use caliptra_drivers::{CaliptraError, CaliptraResult};
+
+pub(crate) fn handle_impactless_update(drivers: &mut Drivers) -> CaliptraResult<()> {
+    let cycles = drivers
+        .soc_ifc
+        .regs_mut()
+        .internal_fw_update_reset_wait_cycles()
+        .read()
+        .into();
+    for _ in 0..cycles {
+        drivers
+            .soc_ifc
+            .regs_mut()
+            .internal_fw_update_reset()
+            .write(|w| w.core_rst(true));
+    }
+
+    Err(CaliptraError::RUNTIME_UNEXPECTED_UPDATE_RETURN)
+}
diff --git a/sw-emulator/lib/periph/src/key_vault.rs b/sw-emulator/lib/periph/src/key_vault.rs
index a30f343d..4c1eccde 100644
--- a/sw-emulator/lib/periph/src/key_vault.rs
+++ b/sw-emulator/lib/periph/src/key_vault.rs
@@ -448,6 +448,8 @@ impl KeyVaultRegs {
         Ok(())
     }
 
+    // Does not write to usage, usage bits aren't directly writable outside of
+    // key creation.
     fn write_key_ctrl(&mut self, _size: RvSize, index: usize, val: u32) -> Result<(), BusError> {
         let val = LocalRegisterCopy::<u32, KV_CONTROL::Register>::new(val);
         let key_ctrl_reg = &mut self.key_control[index];
@@ -462,8 +464,6 @@ impl KeyVaultRegs {
                 .val(key_ctrl_reg.read(KV_CONTROL::USE_LOCK) | val.read(KV_CONTROL::USE_LOCK)),
         );
 
-        key_ctrl_reg.modify(KV_CONTROL::USAGE.val(val.read(KV_CONTROL::USAGE)));
-
         if key_ctrl_reg.read(KV_CONTROL::WRITE_LOCK) == 0 && val.is_set(KV_CONTROL::CLEAR) {
             let key_min = index * KeyVault::KEY_SIZE;
             let key_max = key_min + KeyVault::KEY_SIZE;
diff --git a/sw-emulator/lib/periph/src/soc_reg.rs b/sw-emulator/lib/periph/src/soc_reg.rs
index bfecc876..62b6f9dc 100644
--- a/sw-emulator/lib/periph/src/soc_reg.rs
+++ b/sw-emulator/lib/periph/src/soc_reg.rs
@@ -630,7 +630,7 @@ impl SocRegistersImpl {
             internal_obf_key: args.cptra_obf_key,
             internal_iccm_lock: ReadWriteRegister::new(0),
             internal_fw_update_reset: ReadWriteRegister::new(0),
-            internal_fw_update_reset_wait_cycles: ReadWriteRegister::new(0),
+            internal_fw_update_reset_wait_cycles: ReadWriteRegister::new(5),
             internal_nmi_vector: ReadWriteRegister::new(0),
             mailbox,
             iccm,


Test patch:
diff --git a/drivers/test-fw/src/bin/mailbox_driver_responder.rs b/drivers/test-fw/src/bin/mailbox_driver_responder.rs
index db2aec98..ae4c1cd7 100644
--- a/drivers/test-fw/src/bin/mailbox_driver_responder.rs
+++ b/drivers/test-fw/src/bin/mailbox_driver_responder.rs
@@ -54,6 +54,20 @@ extern "C" fn main() {
             0x8000_0000 => {
                 txn.complete(false).unwrap();
             }
+            // Test dropping first half of words and then printing the remaining words
+            0x9000_0000 => {
+                let dlen = txn.dlen() as usize;
+                let dlen_words = (dlen + 3) / 4;
+                println!("dlen: {dlen}");
+                txn.drop_words(dlen_words / 2).unwrap();
+                let rem_words = dlen_words / 2;
+                let mut buf = [0u32; 1];
+                for _ in 0..rem_words {
+                    txn.copy_request(&mut buf).unwrap();
+                    println!("buf: {:08x?}", buf);
+                }
+                txn.complete(true).unwrap();
+            }
             // Test transaction dropped immediately
             _ => {}
         }
diff --git a/drivers/tests/integration_tests.rs b/drivers/tests/integration_tests.rs
index b8b49386..7e4d9be0 100644
--- a/drivers/tests/integration_tests.rs
+++ b/drivers/tests/integration_tests.rs
@@ -434,6 +434,22 @@ fn test_mailbox_soc_to_uc() {
              buf: [04050607, 00000003]\n"
         );
     }
+
+    // Test drop_words
+    {
+        model
+            .mailbox_execute(
+                0x9000_0000,
+                &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
+            )
+            .unwrap();
+        assert_eq!(
+            model.output().take(usize::MAX),
+            "cmd: 0x90000000\n\
+             dlen: 8\n\
+             buf: [08070605]\n"
+        );
+    }
 }
 
 #[test]
diff --git a/runtime/tests/integration_tests.rs b/runtime/tests/integration_tests.rs
index 4bc42238..7060cddf 100644
--- a/runtime/tests/integration_tests.rs
+++ b/runtime/tests/integration_tests.rs
@@ -1,6 +1,7 @@
 // Licensed under the Apache-2.0 license.
 pub mod common;
 
+use caliptra_builder::{ImageOptions, APP_WITH_UART, FMC_WITH_UART};
 use caliptra_drivers::Ecc384PubKey;
 use caliptra_hw_model::{HwModel, ModelError, ShaAccMode};
 use caliptra_runtime::{CommandId, EcdsaVerifyCmd};
@@ -26,6 +27,34 @@ fn test_standard() {
         .unwrap();
 }
 
+#[test]
+fn test_update() {
+    // Test that the normal runtime firmware boots.
+    // Ultimately, this will be useful for exercising Caliptra end-to-end
+    // via the mailbox.
+    let mut model = run_rt_test(None);
+
+    model.step_until(|m| m.soc_mbox().status().read().mbox_fsm_ps().mbox_idle());
+
+    // Make image to update to
+    let image = caliptra_builder::build_and_sign_image(
+        &FMC_WITH_UART,
+        &APP_WITH_UART,
+        ImageOptions::default(),
+    )
+    .unwrap()
+    .to_bytes()
+    .unwrap();
+
+    model
+        .mailbox_execute(u32::from(CommandId::FIRMWARE_LOAD), &image)
+        .unwrap();
+
+    model
+        .step_until_output_contains("Caliptra RT listening for mailbox commands...")
+        .unwrap();
+}
+
 #[test]
 fn test_boot() {
     let mut model = run_rt_test(Some("boot"));