Repository: chipsalliance/caliptra-sw
Pull request #526 (2023-07-21 01:17:36)

Runtime [feat] FIPS Version Command
This commit implements FIPS Version command handling in the runtime module and adds fw-rev-id and hw-rev-id registers to the emulated model (sw-emulator).

rusty1968: https://github.com/chipsalliance/caliptra-sw/issues/186

Patch:
diff --git a/libcaliptra/.gitignore b/libcaliptra/.gitignore
index 417d1e2e..d8da6622 100644
--- a/libcaliptra/.gitignore
+++ b/libcaliptra/.gitignore
@@ -1,5 +1,3 @@
 inc/caliptra_model.h
-src/caliptra_api.o
-libcaliptra/examples/generic/main.o
-libcaliptra/examples/hwmodel/hwmodel
-libcaliptra/examples/hwmodel/interface.o
+examples/hwmodel/hwmodel
+*.o
diff --git a/libcaliptra/examples/generic/main.c b/libcaliptra/examples/generic/main.c
index 844db4aa..541cae63 100644
--- a/libcaliptra/examples/generic/main.c
+++ b/libcaliptra/examples/generic/main.c
@@ -26,7 +26,7 @@ int main(int argc, char *argv[])
 
     // Initialize FSM GO
     caliptra_bootfsm_go();
-    
+
     // Wait until ready for FW
     caliptra_ready_for_firmware();
 
@@ -34,29 +34,21 @@ int main(int argc, char *argv[])
     // FW_PATH is defined on the compiler command line
     caliptra_upload_fw(&image_bundle);
 
-    uint32_t FIPS_VERSION = 0x46505652;
-
-    int mb_result;
-    uint32_t fips_ver;
-    struct caliptra_buffer buf = {
-        .data = (uint8_t*)&fips_ver,
-        .len = sizeof(fips_ver),
-    };
-
     // Run Until RT is ready to receive commands
+    struct caliptra_fips_version version;
     while(1) {
         caliptra_wait();
-        mb_result = caliptra_mailbox_execute(FIPS_VERSION, &buf, NULL);
-
-        if (mb_result != -EIO)
+        status = caliptra_get_fips_version(&version);
+        if (status)
         {
-            printf("Caliptra C API Integration Test Failed: %x\n", mb_result);
-            return -1;
+            printf("Caliptra C API Integration Test Failed: %x\n", status);
+            return status;
         }
 
         break;
     }
-    printf("Caliptra C API Integration Test Passed \n");
+    printf("Caliptra C API Integration Test Passed: \n\tFIPS_VERSION = mode: 0x%x, fips_rev (0x%x, 0x%x, 0x%x), name %s \n", version.mode,
+                version.fips_rev[0], version.fips_rev[1], version.fips_rev[2], version.name);
     return 0;
 }
 
diff --git a/libcaliptra/inc/caliptra_api.h b/libcaliptra/inc/caliptra_api.h
index d64a3467..d13a29f2 100644
--- a/libcaliptra/inc/caliptra_api.h
+++ b/libcaliptra/inc/caliptra_api.h
@@ -50,6 +50,12 @@ struct caliptra_fuses {
     enum DeviceLifecycle life_cycle;
 };
 
+struct caliptra_fips_version {
+    uint32_t mode;
+    uint32_t fips_rev[3];
+    uint8_t name[12];
+};
+
 // Query if ROM is ready for fuses
 bool caliptra_ready_for_fuses(void);
 
@@ -65,5 +71,8 @@ bool caliptra_ready_for_firmware(void);
 // Upload Caliptra Firmware
 int caliptra_upload_fw(struct caliptra_buffer *fw_buffer);
 
+// Read Caliptra FIPS Version
+int caliptra_get_fips_version(struct caliptra_fips_version *version);
+
 // Execute Mailbox Command
 int caliptra_mailbox_execute(uint32_t cmd, struct caliptra_buffer *mbox_tx_buffer, struct caliptra_buffer *mbox_rx_buffer);
diff --git a/libcaliptra/src/caliptra_api.c b/libcaliptra/src/caliptra_api.c
index 6128cebd..d7f14832 100644
--- a/libcaliptra/src/caliptra_api.c
+++ b/libcaliptra/src/caliptra_api.c
@@ -17,7 +17,7 @@ static inline uint32_t caliptra_read_status(void)
     uint32_t status;
 
     caliptra_read_u32(CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS, &status);
-   
+
     return status;
 }
 
@@ -30,11 +30,12 @@ static inline uint32_t caliptra_read_status(void)
  */
 bool caliptra_ready_for_fuses(void)
 {
-    if ((caliptra_read_status() & GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS_READY_FOR_FUSES_MASK) != 0)
+    if ((caliptra_read_status() & GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS_READY_FOR_FUSES_MASK) != 0) {
         return true;
+    }
 
     return false;
- }
+}
 
 /**
  * caliptra_init_fuses
@@ -48,7 +49,8 @@ bool caliptra_ready_for_fuses(void)
 int caliptra_init_fuses(struct caliptra_fuses *fuses)
 {
     // Parameter check
-    if (!fuses) {
+    if (!fuses)
+    {
         return -EINVAL;
     }
 
@@ -63,7 +65,7 @@ int caliptra_init_fuses(struct caliptra_fuses *fuses)
     caliptra_fuse_write(GENERIC_AND_FUSE_REG_FUSE_KEY_MANIFEST_PK_HASH_MASK, fuses->key_manifest_pk_hash_mask);
     caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_OWNER_PK_HASH_0, fuses->owner_pk_hash, sizeof(fuses->owner_pk_hash));
     caliptra_fuse_write(GENERIC_AND_FUSE_REG_FUSE_FMC_KEY_MANIFEST_SVN, fuses->fmc_key_manifest_svn);
-    caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_FMC_KEY_MANIFEST_SVN, fuses->runtime_svn, sizeof(fuses->runtime_svn)); // https://github.com/chipsalliance/caliptra-sw/issues/529 
+    caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_FMC_KEY_MANIFEST_SVN, fuses->runtime_svn, sizeof(fuses->runtime_svn)); // https://github.com/chipsalliance/caliptra-sw/issues/529
     caliptra_fuse_write(GENERIC_AND_FUSE_REG_FUSE_ANTI_ROLLBACK_DISABLE, (uint32_t)fuses->anti_rollback_disable);
     caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_IDEVID_CERT_ATTR_0, fuses->idevid_cert_attr, sizeof(fuses->idevid_cert_attr));
     caliptra_fuse_array_write(GENERIC_AND_FUSE_REG_FUSE_IDEVID_MANUF_HSM_ID_0, fuses->idevid_manuf_hsm_id, sizeof(fuses->idevid_manuf_hsm_id));
@@ -91,7 +93,7 @@ int caliptra_bootfsm_go()
     // Write BOOTFSM_GO Register
     caliptra_write_u32(CALIPTRA_TOP_REG_GENERIC_AND_FUSE_REG_CPTRA_BOOTFSM_GO, 1);
 
-    //TODO: Check registers/provide async completion mechanism
+    // TODO: Check registers/provide async completion mechanism
 
     return 0;
 }
@@ -110,24 +112,48 @@ static int caliptra_mailbox_write_fifo(struct caliptra_buffer *buffer)
     // Check against max size
     const uint32_t MBOX_SIZE = (128u * 1024u);
 
-    if (buffer->len > MBOX_SIZE) {
+    // Check if buffer is not null.
+    if (buffer == NULL)
+    {
         return -EINVAL;
     }
 
-    // Write DLEN
+    if (buffer->len > MBOX_SIZE)
+    {
+        return -EINVAL;
+    }
+
+    // Write DLEN to transition to the next state.
     caliptra_mbox_write_dlen(buffer->len);
 
+    if (buffer->len == 0)
+    {
+        // We can return early, there is no payload.
+        // dlen needs to be written to transition the state machine,
+        // even if it is zero.
+        return 0;
+    }
+
+    // We have data to write, better check if have a place to read it
+    // from.
+    if (buffer->data == NULL)
+    {
+        return -EINVAL;
+    }
+
     uint32_t remaining_len = buffer->len;
     uint32_t *data_dw = (uint32_t *)buffer->data;
 
     // Copy DWord multiples
-    while (remaining_len > sizeof(uint32_t)) {
+    while (remaining_len > sizeof(uint32_t))
+    {
         caliptra_mbox_write(MBOX_CSR_MBOX_DATAIN, *data_dw++);
         remaining_len -= sizeof(uint32_t);
     }
 
     // if un-aligned dword remainder...
-    if (remaining_len) {
+    if (remaining_len)
+    {
         uint32_t data = 0;
         memcpy(&data, data_dw, remaining_len);
         caliptra_mbox_write(MBOX_CSR_MBOX_DATAIN, data);
@@ -155,22 +181,23 @@ static int caliptra_mailbox_read_buffer(struct caliptra_buffer *buffer)
 
     // Check we have enough room in the buffer
     if (buffer->len < remaining_len || !buffer->data)
-       return -EINVAL;
+        return -EINVAL;
 
     uint32_t *data_dw = (uint32_t *)buffer->data;
 
     // Copy DWord multiples
-    while (remaining_len > sizeof(uint32_t)) {
+    while (remaining_len >= sizeof(uint32_t))
+    {
         *data_dw++ = caliptra_mbox_read(MBOX_CSR_MBOX_DATAOUT);
         remaining_len -= sizeof(uint32_t);
     }
 
     // if un-aligned dword reminder...
-    if (remaining_len) {
+    if (remaining_len)
+    {
         uint32_t data = caliptra_mbox_read(MBOX_CSR_MBOX_DATAOUT);
         memcpy(data_dw, &data, remaining_len);
     }
-
     return 0;
 }
 
@@ -188,34 +215,43 @@ static int caliptra_mailbox_read_buffer(struct caliptra_buffer *buffer)
 int caliptra_mailbox_execute(uint32_t cmd, struct caliptra_buffer *mbox_tx_buffer, struct caliptra_buffer *mbox_rx_buffer)
 {
     // If mbox already locked return
-    if (caliptra_mbox_is_lock()) {
+    if (caliptra_mbox_is_lock())
+    {
         return -EBUSY;
     }
-                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
+
     // Write Cmd and Tx Buffer
     caliptra_mbox_write_cmd(cmd);
     caliptra_mailbox_write_fifo(mbox_tx_buffer);
 
     // Set Execute bit and wait
-    caliptra_mbox_write_execute_wait(true);
+    caliptra_mbox_write_execute_busy_wait(true);
 
     // Check the Mailbox Status
     uint32_t status = caliptra_mbox_read_status();
-    if (status == CALIPTRA_MBOX_STATUS_CMD_FAILURE) {
+    if (status == CALIPTRA_MBOX_STATUS_CMD_FAILURE)
+    {
         caliptra_mbox_write_execute(false);
         return -EIO;
-    } else if(status == CALIPTRA_MBOX_STATUS_CMD_COMPLETE) {
+    }
+    else if (status == CALIPTRA_MBOX_STATUS_CMD_COMPLETE)
+    {
         caliptra_mbox_write_execute(false);
         return 0;
-    } else if (status != CALIPTRA_MBOX_STATUS_DATA_READY) {
+    }
+    else if (status != CALIPTRA_MBOX_STATUS_DATA_READY)
+    {
         return -EIO;
     }
 
     // Read Buffer
     caliptra_mailbox_read_buffer(mbox_rx_buffer);
 
-    // Execute False and wait
-    caliptra_mbox_write_execute_wait(false);
+    // Execute False
+    caliptra_mbox_write_execute(false);
+
+    // Wait
+    caliptra_wait();
 
     if (caliptra_mbox_read_status_fsm() != CALIPTRA_MBOX_STATUS_FSM_IDLE)
         return -EIO;
@@ -226,21 +262,20 @@ int caliptra_mailbox_execute(uint32_t cmd, struct caliptra_buffer *mbox_tx_buffe
 /**
  * caliptra_ready_for_firmware
  *
- * Reports if the Caliptra hardware is ready for firmware upload 
+ * Reports if the Caliptra hardware is ready for firmware upload
  *
  * @return bool True if ready, false otherwise
  */
 bool caliptra_ready_for_firmware(void)
 {
     uint32_t status;
-    bool     ready;
+    bool ready;
 
     do
     {
         status = caliptra_read_status();
 
-        if ((status & GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS_READY_FOR_FW_MASK) 
-                == GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS_READY_FOR_FW_MASK)
+        if ((status & GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS_READY_FOR_FW_MASK) == GENERIC_AND_FUSE_REG_CPTRA_FLOW_STATUS_READY_FOR_FW_MASK)
         {
             ready = true;
         }
@@ -248,7 +283,7 @@ bool caliptra_ready_for_firmware(void)
         {
             caliptra_wait();
         }
-    } while(ready == false);
+    } while (ready == false);
 
     return true;
 }
@@ -257,13 +292,46 @@ bool caliptra_ready_for_firmware(void)
  * caliptra_upload_fw
  *
  * Upload firmware to the Caliptra device
- * 
+ *
  * @param[in] fw_buffer Buffer containing Caliptra firmware
  *
- * @return See caliptra_mailbo, mb_resultx_execute for possible results.
+ * @return See caliptra_mailbox, mb_resultx_execute for possible results.
  */
 int caliptra_upload_fw(struct caliptra_buffer *fw_buffer)
 {
+    // Parameter check
+    if (fw_buffer == NULL)
+        return -EINVAL;
+
     const uint32_t FW_LOAD_CMD_OPCODE = 0x46574C44u;
     return caliptra_mailbox_execute(FW_LOAD_CMD_OPCODE, fw_buffer, NULL);
 }
+
+/**
+ * caliptra_get_fips_version
+ *
+ * Read Caliptra FIPS Version
+ *
+ * @param[out] version pointer to fips_version unsigned integer
+ *
+ * @return See caliptra_mailbox, mb_resultx_execute for possible results.
+ */
+int caliptra_get_fips_version(struct caliptra_fips_version *version)
+{
+    // Parameter check
+    if (version == NULL)
+        return -EINVAL;
+
+    uint32_t FIPS_VERSION_OPCODE = 0x46505652;
+
+    struct caliptra_buffer in_buf = {
+        .data = NULL,
+        .len = 0,
+    };
+    struct caliptra_buffer out_buf = {
+        .data = (uint8_t *)version,
+        .len = sizeof(struct caliptra_fips_version),
+    };
+
+    return caliptra_mailbox_execute(FIPS_VERSION_OPCODE, &in_buf, &out_buf);
+}
diff --git a/libcaliptra/src/caliptra_mbox.h b/libcaliptra/src/caliptra_mbox.h
index 69f09ff8..8640160c 100644
--- a/libcaliptra/src/caliptra_mbox.h
+++ b/libcaliptra/src/caliptra_mbox.h
@@ -49,7 +49,7 @@ static inline void caliptra_mbox_write_execute(bool ex)
     caliptra_mbox_write(MBOX_CSR_MBOX_EXECUTE, ex);
 }
 
-static inline uint8_t caliptra_mbox_write_execute_wait(bool ex)
+static inline uint8_t caliptra_mbox_write_execute_busy_wait(bool ex)
 {
     caliptra_mbox_write(MBOX_CSR_MBOX_EXECUTE, ex);
     uint8_t status;
diff --git a/runtime/src/fips.rs b/runtime/src/fips.rs
index 4651f9d4..3e9324b9 100644
--- a/runtime/src/fips.rs
+++ b/runtime/src/fips.rs
@@ -1,18 +1,48 @@
 // Licensed under the Apache-2.0 license
+
 use caliptra_common::cprintln;
 use caliptra_drivers::CaliptraError;
 use caliptra_drivers::CaliptraResult;
 use caliptra_registers::mbox::enums::MboxStatusE;
+use zerocopy::{AsBytes, FromBytes};
 
 use crate::Drivers;
 
-/// Fips command handler.
 pub struct FipsModule;
 
+#[repr(C)]
+#[derive(Clone, Debug, Default, AsBytes, FromBytes)]
+pub struct VersionResponse {
+    pub mode: u32,
+    pub fips_rev: [u32; 3],
+    pub name: [u8; 12],
+}
+
+impl VersionResponse {
+    pub const NAME: [u8; 12] = *b"Caliptra RTM";
+    pub const MODE: u32 = 0x46495053;
+
+    pub fn new(_env: &Drivers) -> Self {
+        Self {
+            mode: Self::MODE,
+            // Just return all zeroes for now.
+            fips_rev: [1, 0, 0],
+            name: Self::NAME,
+        }
+    }
+    pub fn copy_to_mbox(&self, env: &mut Drivers) -> CaliptraResult<()> {
+        let mbox = &mut env.mbox;
+        mbox.write_response(self.as_bytes())
+    }
+}
+
+/// Fips command handler.
 impl FipsModule {
-    pub fn version(_env: &Drivers) -> CaliptraResult<MboxStatusE> {
+    pub fn version(env: &mut Drivers) -> CaliptraResult<MboxStatusE> {
         cprintln!("[rt] FIPS Version");
-        Err(CaliptraError::RUNTIME_FIPS_UNIMPLEMENTED)
+
+        VersionResponse::new(env).copy_to_mbox(env)?;
+        Ok(MboxStatusE::DataReady)
     }
 
     pub fn self_test(_env: &Drivers) -> CaliptraResult<MboxStatusE> {
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 5626e2b0..c5e4fe72 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -13,7 +13,7 @@ pub mod mailbox;
 use mailbox::Mailbox;
 
 pub mod packet;
-pub use fips::FipsModule;
+pub use fips::{FipsModule, VersionResponse};
 use packet::Packet;
 
 use caliptra_common::memory_layout::{


Test patch:
diff --git a/runtime/tests/integration_tests.rs b/runtime/tests/integration_tests.rs
index f7d1f5f8..5abb3465 100644
--- a/runtime/tests/integration_tests.rs
+++ b/runtime/tests/integration_tests.rs
@@ -4,7 +4,7 @@ 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};
+use caliptra_runtime::{CommandId, EcdsaVerifyCmd, VersionResponse};
 use common::{run_rom_test, run_rt_test};
 use openssl::{
     bn::BigNum,
@@ -196,8 +196,21 @@ fn test_fips_cmd_api() {
     let cmd = [0u8; 4];
 
     let resp = model.mailbox_execute(u32::from(CommandId::VERSION), &cmd);
-    assert_eq!(resp, expected_err);
-    model.soc_ifc().cptra_fw_error_non_fatal().write(|_| 0);
+    assert!(resp.is_ok());
+    let fips_version_resp = model
+        .mailbox_execute(u32::from(CommandId::VERSION), &cmd)
+        .unwrap()
+        .unwrap();
+
+    // Check command size
+    let fips_version_bytes: &[u8] = fips_version_resp.as_bytes();
+
+    // Check values against expected.
+    let fips_version = VersionResponse::read_from(fips_version_bytes.as_bytes()).unwrap();
+    assert_eq!(fips_version.mode, VersionResponse::MODE);
+    assert_eq!(fips_version.fips_rev, [0x01, 0x00, 0x00]);
+    let name = &fips_version.name[..];
+    assert_eq!(name, VersionResponse::NAME.as_bytes());
 
     let resp = model.mailbox_execute(u32::from(CommandId::SELF_TEST), &cmd);
     assert_eq!(resp, expected_err);