55use std:: collections:: BTreeMap ;
66use std:: io:: Cursor ;
77use std:: mem;
8+ use std:: time:: Duration ;
9+ use std:: time:: Instant ;
810
911use crate :: SIM_GIMLET_BOARD ;
1012use crate :: SIM_ROT_BOARD ;
@@ -13,6 +15,7 @@ use crate::SIM_SIDECAR_BOARD;
1315use crate :: helpers:: rot_slot_id_from_u16;
1416use crate :: helpers:: rot_slot_id_to_u16;
1517use gateway_messages:: Fwid ;
18+ use gateway_messages:: HfError ;
1619use gateway_messages:: RotSlotId ;
1720use gateway_messages:: RotStateV3 ;
1821use gateway_messages:: SpComponent ;
@@ -21,9 +24,14 @@ use gateway_messages::UpdateChunk;
2124use gateway_messages:: UpdateId ;
2225use gateway_messages:: UpdateInProgressStatus ;
2326use hubtools:: RawHubrisImage ;
27+ use sha2:: Sha256 ;
2428use sha3:: Digest ;
2529use sha3:: Sha3_256 ;
2630
31+ // How long do we take to hash host flash? Real SPs take a handful of seconds;
32+ // we'll pick something similar.
33+ const TIME_TO_HASH_HOST_PHASE_1 : Duration = Duration :: from_secs ( 5 ) ;
34+
2735pub ( crate ) struct SimSpUpdate {
2836 /// tracks the state of any ongoing simulated update
2937 ///
@@ -38,6 +46,8 @@ pub(crate) struct SimSpUpdate {
3846 /// data from the last completed phase1 update for each slot (exposed for
3947 /// testing)
4048 last_host_phase1_update_data : BTreeMap < u16 , Box < [ u8 ] > > ,
49+ /// state of hashing each of the host phase1 slots
50+ phase1_hash_state : BTreeMap < u16 , HostFlashHashState > ,
4151
4252 /// records whether a change to the stage0 "active slot" has been requested
4353 pending_stage0_update : bool ,
@@ -177,6 +187,7 @@ impl SimSpUpdate {
177187 last_sp_update_data : None ,
178188 last_rot_update_data : None ,
179189 last_host_phase1_update_data : BTreeMap :: new ( ) ,
190+ phase1_hash_state : BTreeMap :: new ( ) ,
180191
181192 pending_stage0_update : false ,
182193
@@ -303,6 +314,13 @@ impl SimSpUpdate {
303314 std:: io:: Write :: write_all ( data, chunk_data)
304315 . map_err ( |_| SpError :: UpdateIsTooLarge ) ?;
305316
317+ // If we're writing to the host flash, invalidate the cached
318+ // hash of this slot.
319+ if * component == SpComponent :: HOST_CPU_BOOT_FLASH {
320+ self . phase1_hash_state
321+ . insert ( * slot, HostFlashHashState :: HashInvalidated ) ;
322+ }
323+
306324 if data. position ( ) == data. get_ref ( ) . len ( ) as u64 {
307325 let mut stolen = Cursor :: new ( Box :: default ( ) ) ;
308326 mem:: swap ( data, & mut stolen) ;
@@ -452,6 +470,78 @@ impl SimSpUpdate {
452470 self . last_host_phase1_update_data . get ( & slot) . cloned ( )
453471 }
454472
473+ pub ( crate ) fn start_host_flash_hash (
474+ & mut self ,
475+ slot : u16 ,
476+ ) -> Result < ( ) , SpError > {
477+ match self
478+ . phase1_hash_state
479+ . entry ( slot)
480+ . or_insert ( HostFlashHashState :: NeverHashed )
481+ {
482+ // No current hash; record our start time so we can emulate hashing
483+ // taking a few seconds.
484+ state @ ( HostFlashHashState :: NeverHashed
485+ | HostFlashHashState :: HashInvalidated ) => {
486+ * state = HostFlashHashState :: HashStarted ( Instant :: now ( ) ) ;
487+ Ok ( ( ) )
488+ }
489+ // Already hashed; this is a no-op.
490+ HostFlashHashState :: Hashed ( _) => Ok ( ( ) ) ,
491+ // Still hashing; check and see if it's done. This is either an
492+ // error (if we're still hashing) or a no-op (if we're done).
493+ HostFlashHashState :: HashStarted ( started) => {
494+ let started = * started;
495+ self . finalize_host_flash_hash_if_sufficient_time_elapsed (
496+ slot, started,
497+ ) ?;
498+ Ok ( ( ) )
499+ }
500+ }
501+ }
502+
503+ pub ( crate ) fn get_host_flash_hash (
504+ & mut self ,
505+ slot : u16 ,
506+ ) -> Result < [ u8 ; 32 ] , SpError > {
507+ match self
508+ . phase1_hash_state
509+ . entry ( slot)
510+ . or_insert ( HostFlashHashState :: NeverHashed )
511+ {
512+ HostFlashHashState :: NeverHashed => {
513+ Err ( SpError :: Hf ( HfError :: HashUncalculated ) )
514+ }
515+ HostFlashHashState :: HashStarted ( started) => {
516+ let started = * started;
517+ self . finalize_host_flash_hash_if_sufficient_time_elapsed (
518+ slot, started,
519+ )
520+ }
521+ HostFlashHashState :: Hashed ( hash) => Ok ( * hash) ,
522+ HostFlashHashState :: HashInvalidated => {
523+ Err ( SpError :: Hf ( HfError :: RecalculateHash ) )
524+ }
525+ }
526+ }
527+
528+ fn finalize_host_flash_hash_if_sufficient_time_elapsed (
529+ & mut self ,
530+ slot : u16 ,
531+ started : Instant ,
532+ ) -> Result < [ u8 ; 32 ] , SpError > {
533+ if started. elapsed ( ) < TIME_TO_HASH_HOST_PHASE_1 {
534+ return Err ( SpError :: Hf ( HfError :: HashInProgress ) ) ;
535+ }
536+
537+ let data = self . last_host_phase1_update_data ( slot) ;
538+ let data = data. as_deref ( ) . unwrap_or ( & [ ] ) ;
539+ let hash = Sha256 :: digest ( & data) . into ( ) ;
540+ self . phase1_hash_state . insert ( slot, HostFlashHashState :: Hashed ( hash) ) ;
541+
542+ Ok ( hash)
543+ }
544+
455545 pub ( crate ) fn get_component_caboose_value (
456546 & mut self ,
457547 component : SpComponent ,
@@ -663,3 +753,11 @@ fn fake_fwid_compute(data: &[u8]) -> Fwid {
663753 digest. update ( data) ;
664754 Fwid :: Sha3_256 ( digest. finalize ( ) . into ( ) )
665755}
756+
757+ #[ derive( Debug , Clone , Copy ) ]
758+ enum HostFlashHashState {
759+ NeverHashed ,
760+ HashStarted ( Instant ) ,
761+ Hashed ( [ u8 ; 32 ] ) ,
762+ HashInvalidated ,
763+ }
0 commit comments