@@ -789,6 +789,36 @@ pub const MCU_TEST_IV: [u8; 12] = [
789789/// Stash Measurement Command Opcode.
790790const STASH_MEASUREMENT_CMD_OPCODE : u32 = 0x4D45_4153 ;
791791
792+ const SUBSYSTEM_MBOX_LOCK_MAX_ATTEMPTS : u32 = 100 ;
793+ const SUBSYSTEM_MBOX_LOCK_STEPS_PER_ATTEMPT : u32 = 1000 ;
794+
795+ /// Operations needed by [`retry_acquire_mailbox_lock`], abstracted so the retry
796+ /// policy can be unit-tested without a hardware model.
797+ trait MailboxLockOps {
798+ /// Returns `true` if the mailbox lock is now held by us.
799+ fn try_acquire_lock ( & mut self ) -> bool ;
800+ /// Advance the model so another agent can release the lock.
801+ fn step ( & mut self ) ;
802+ }
803+
804+ /// Acquire the mailbox lock, retrying while another agent holds it. Returns
805+ /// `true` if acquired within `max_attempts`.
806+ fn retry_acquire_mailbox_lock (
807+ ops : & mut impl MailboxLockOps ,
808+ max_attempts : u32 ,
809+ steps_per_attempt : u32 ,
810+ ) -> bool {
811+ for _ in 0 ..max_attempts {
812+ if ops. try_acquire_lock ( ) {
813+ return true ;
814+ }
815+ for _ in 0 ..steps_per_attempt {
816+ ops. step ( ) ;
817+ }
818+ }
819+ false
820+ }
821+
792822// Represents a emulator or simulation of the caliptra hardware, to be called
793823// from tests. Typically, test cases should use [`crate::new()`] to create a model
794824// based on the cargo features (and any model-specific environment variables).
@@ -1271,8 +1301,28 @@ pub trait HwModel: SocManager {
12711301 cmd : u32 ,
12721302 buf : & [ u8 ] ,
12731303 ) -> std:: result:: Result < ( ) , ModelError > {
1274- // Read a 0 to get the lock
1275- if self . soc_mbox ( ) . lock ( ) . read ( ) . lock ( ) {
1304+ // In the FPGA subsystem profile the MCU ROM performs post-runtime
1305+ // mailbox activity that briefly races host commands. Retry the lock,
1306+ // stepping the model so the MCU can release it.
1307+ if self . subsystem_mode ( ) {
1308+ struct ModelLock < ' a , M : HwModel + ?Sized > ( & ' a mut M ) ;
1309+ impl < M : HwModel + ?Sized > MailboxLockOps for ModelLock < ' _ , M > {
1310+ fn try_acquire_lock ( & mut self ) -> bool {
1311+ !self . 0 . soc_mbox ( ) . lock ( ) . read ( ) . lock ( )
1312+ }
1313+ fn step ( & mut self ) {
1314+ self . 0 . step ( ) ;
1315+ }
1316+ }
1317+ let acquired = retry_acquire_mailbox_lock (
1318+ & mut ModelLock ( & mut * self ) ,
1319+ SUBSYSTEM_MBOX_LOCK_MAX_ATTEMPTS ,
1320+ SUBSYSTEM_MBOX_LOCK_STEPS_PER_ATTEMPT ,
1321+ ) ;
1322+ if !acquired {
1323+ return Err ( ModelError :: UnableToLockMailbox ) ;
1324+ }
1325+ } else if self . soc_mbox ( ) . lock ( ) . read ( ) . lock ( ) {
12761326 return Err ( ModelError :: UnableToLockMailbox ) ;
12771327 }
12781328
@@ -1731,6 +1781,48 @@ mod tests {
17311781 rv32_gen. into_inner ( ) . empty_loop ( ) . build ( )
17321782 }
17331783
1784+ #[ test]
1785+ fn test_retry_acquire_mailbox_lock ( ) {
1786+ use crate :: { retry_acquire_mailbox_lock, MailboxLockOps } ;
1787+
1788+ // Fake lock held by another agent until `free_after_steps` steps elapse.
1789+ struct FakeLock {
1790+ free_after_steps : u32 ,
1791+ steps : u32 ,
1792+ }
1793+ impl MailboxLockOps for FakeLock {
1794+ fn try_acquire_lock ( & mut self ) -> bool {
1795+ self . steps >= self . free_after_steps
1796+ }
1797+ fn step ( & mut self ) {
1798+ self . steps += 1 ;
1799+ }
1800+ }
1801+
1802+ // Free immediately: acquired without stepping.
1803+ let mut lock = FakeLock {
1804+ free_after_steps : 0 ,
1805+ steps : 0 ,
1806+ } ;
1807+ assert ! ( retry_acquire_mailbox_lock( & mut lock, 100 , 1000 ) ) ;
1808+ assert_eq ! ( lock. steps, 0 ) ;
1809+
1810+ // Released within the retry budget.
1811+ let mut lock = FakeLock {
1812+ free_after_steps : 2500 ,
1813+ steps : 0 ,
1814+ } ;
1815+ assert ! ( retry_acquire_mailbox_lock( & mut lock, 100 , 1000 ) ) ;
1816+
1817+ // Never released: fails after exhausting all attempts.
1818+ let mut lock = FakeLock {
1819+ free_after_steps : u32:: MAX ,
1820+ steps : 0 ,
1821+ } ;
1822+ assert ! ( !retry_acquire_mailbox_lock( & mut lock, 5 , 10 ) ) ;
1823+ assert_eq ! ( lock. steps, 50 ) ;
1824+ }
1825+
17341826 #[ test]
17351827 fn test_axi ( ) {
17361828 let mut model = caliptra_hw_model:: new_unbooted ( InitParams {
0 commit comments