Repository: chipsalliance/caliptra-sw
Pull request #184 (2023-04-17 19:57:11)

Fix emulator mailbox peripheral to support response data and add mailbox command test for all HwModels
test_mailbox_execute() runs as a presubmit against both verilator and sw-emulator.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 30f0995d..200c9955 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -280,6 +280,7 @@ dependencies = [
 name = "caliptra-hw-model-test-fw"
 version = "0.1.0"
 dependencies = [
+ "caliptra-registers",
  "caliptra-test-harness",
 ]
 
diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs
index 207c04f8..d602bebe 100644
--- a/hw-model/src/lib.rs
+++ b/hw-model/src/lib.rs
@@ -446,7 +446,8 @@ pub trait HwModel {
 
 #[cfg(test)]
 mod tests {
-    use crate::{mmio::Rv32GenMmio, BootParams, HwModel, InitParams};
+    use crate::{mmio::Rv32GenMmio, BootParams, HwModel, InitParams, ModelError};
+    use caliptra_builder::FwId;
     use caliptra_emu_bus::Bus;
     use caliptra_emu_types::RvSize;
     use caliptra_registers::soc_ifc;
@@ -549,4 +550,65 @@ mod tests {
             "expected output \"ha\", was \"hi\""
         );
     }
+
+    #[test]
+    pub fn test_mailbox_execute() {
+        let message: [u8; 10] = [0x90, 0x5e, 0x1f, 0xad, 0x8b, 0x60, 0xb0, 0xbf, 0x1c, 0x7e];
+
+        let rom = caliptra_builder::build_firmware_rom(&FwId {
+            crate_name: "caliptra-hw-model-test-fw",
+            bin_name: "mailbox_responder",
+            features: &["emu"],
+        })
+        .unwrap();
+
+        let mut model = caliptra_hw_model::new(BootParams {
+            init_params: InitParams {
+                rom: &rom,
+                ..Default::default()
+            },
+            ..Default::default()
+        })
+        .unwrap();
+
+        // Send command that echoes the command and input message
+        assert_eq!(
+            model.mailbox_execute(0x1000_0000, &message),
+            Ok(Some(
+                [[0x00, 0x00, 0x00, 0x10].as_slice(), &message].concat()
+            )),
+        );
+
+        // Send command that echoes the command and input message
+        assert_eq!(
+            model.mailbox_execute(0x1000_0000, &message[..8]),
+            Ok(Some(vec![
+                0x00, 0x00, 0x00, 0x10, 0x90, 0x5e, 0x1f, 0xad, 0x8b, 0x60, 0xb0, 0xbf
+            ])),
+        );
+
+        // Send command that returns 7 bytes of output
+        assert_eq!(
+            model.mailbox_execute(0x1000_1000, &[]),
+            Ok(Some(vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd]))
+        );
+
+        // Send command that returns 7 bytes of output, and doesn't consume input
+        assert_eq!(
+            model.mailbox_execute(0x1000_1000, &[42]),
+            Ok(Some(vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd])),
+        );
+
+        // Send command that returns 0 bytes of output
+        assert_eq!(model.mailbox_execute(0x1000_2000, &[]), Ok(Some(vec![])));
+
+        // Send command that returns success with no output
+        assert_eq!(model.mailbox_execute(0x2000_0000, &[]), Ok(None));
+
+        // Send command that returns failure
+        assert_eq!(
+            model.mailbox_execute(0x4000_0000, &message),
+            Err(ModelError::MailboxCmdFailed)
+        );
+    }
 }
diff --git a/sw-emulator/lib/periph/src/mailbox.rs b/sw-emulator/lib/periph/src/mailbox.rs
index 556ea87d..4fb766ff 100644
--- a/sw-emulator/lib/periph/src/mailbox.rs
+++ b/sw-emulator/lib/periph/src/mailbox.rs
@@ -418,6 +418,8 @@ statemachine! {
         RdyForData + DataWrite(DataIn) / enqueue = RdyForData,
         RdyForData + ExecSet = Exec,
         Exec + DataRead / dequeue = Exec,
+        Exec + DlenWrite(DataLength) / init_dlen = Exec,
+        Exec + DataWrite(DataIn) / enqueue = Exec,
         Exec + ExecClear [is_locked] / unlock = Idle
     }
 }
@@ -477,6 +479,7 @@ impl StateMachineContext for Context {
     }
     // actions
     fn init_dlen(&mut self, data_len: &DataLength) {
+        self.ring_buffer.reset();
         self.dlen = data_len.0;
     }
 


Test patch:
diff --git a/hw-model/test-fw/Cargo.toml b/hw-model/test-fw/Cargo.toml
index 4af23671..9536480e 100644
--- a/hw-model/test-fw/Cargo.toml
+++ b/hw-model/test-fw/Cargo.toml
@@ -12,6 +12,7 @@ emu = ["caliptra-test-harness/emu"]
 
 [dependencies]
 caliptra-test-harness = { path = "../../test-harness" }
+caliptra-registers = { path = "../../registers" }
 
 [[bin]]
 name = "test_iccm_unaligned_write"
@@ -21,4 +22,9 @@ required-features = ["riscv"]
 [[bin]]
 name = "test_iccm_byte_write"
 path = "test_iccm_byte_write.rs"
+required-features = ["riscv"]
+
+[[bin]]
+name = "mailbox_responder"
+path = "mailbox_responder.rs"
 required-features = ["riscv"]
\ No newline at end of file
diff --git a/hw-model/test-fw/mailbox_responder.rs b/hw-model/test-fw/mailbox_responder.rs
new file mode 100644
index 00000000..973029c6
--- /dev/null
+++ b/hw-model/test-fw/mailbox_responder.rs
@@ -0,0 +1,69 @@
+// Licensed under the Apache-2.0 license
+
+//! A very simple program that responds to the mailbox.
+
+#![no_main]
+#![no_std]
+
+// Needed to bring in startup code
+#[allow(unused)]
+use caliptra_test_harness;
+
+use caliptra_registers;
+
+#[panic_handler]
+pub fn panic(_info: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+#[no_mangle]
+extern "C" fn main() {
+    let soc_ifc = caliptra_registers::soc_ifc::RegisterBlock::soc_ifc_reg();
+    soc_ifc.cptra_flow_status().write(|w| w.ready_for_fw(true));
+    let mbox = caliptra_registers::mbox::RegisterBlock::mbox_csr();
+
+    loop {
+        while !mbox.status().read().mbox_fsm_ps().mbox_execute_uc() {}
+        let cmd = mbox.cmd().read();
+
+        match cmd {
+            // Consumes input, and echoes the request back as the response with
+            // the command-id prepended.
+            0x1000_0000 => {
+                let dlen = mbox.dlen().read();
+                let dlen_words = usize::try_from((dlen + 3) / 4).unwrap();
+                let mut buf = [0u32; 8];
+                for i in 0..dlen_words {
+                    buf[i] = mbox.dataout().read();
+                }
+                mbox.dlen().write(|_| dlen + 4);
+                mbox.datain().write(|_| cmd);
+                for i in 0..dlen_words {
+                    mbox.datain().write(|_| buf[i]);
+                }
+                mbox.status().write(|w| w.status(|w| w.data_ready()));
+            }
+            // Returns a response of 7 hard-coded bytes; doesn't consume input.
+            0x1000_1000 => {
+                mbox.dlen().write(|_| 7);
+                mbox.datain().write(|_| 0x6745_2301);
+                mbox.datain().write(|_| 0xefcd_ab89);
+
+                mbox.status().write(|w| w.status(|w| w.data_ready()));
+            }
+            // Returns a response of 0 bytes; doesn't consume input.
+            0x1000_2000 => {
+                mbox.dlen().write(|_| 0);
+                mbox.status().write(|w| w.status(|w| w.data_ready()));
+            }
+            // Returns a success response; doesn't consume input.
+            0x2000_0000 => {
+                mbox.status().write(|w| w.status(|w| w.cmd_complete()));
+            }
+            // Everything else returns a failure response; doesn't consume input.
+            _ => {
+                mbox.status().write(|w| w.status(|w| w.cmd_failure()));
+            }
+        }
+    }
+}