5
5
use std:: collections:: BTreeMap ;
6
6
use std:: io:: Cursor ;
7
7
use std:: mem;
8
+ use std:: time:: Duration ;
9
+ use std:: time:: Instant ;
8
10
9
11
use crate :: SIM_GIMLET_BOARD ;
10
12
use crate :: SIM_ROT_BOARD ;
@@ -13,6 +15,7 @@ use crate::SIM_SIDECAR_BOARD;
13
15
use crate :: helpers:: rot_slot_id_from_u16;
14
16
use crate :: helpers:: rot_slot_id_to_u16;
15
17
use gateway_messages:: Fwid ;
18
+ use gateway_messages:: HfError ;
16
19
use gateway_messages:: RotSlotId ;
17
20
use gateway_messages:: RotStateV3 ;
18
21
use gateway_messages:: SpComponent ;
@@ -21,9 +24,14 @@ use gateway_messages::UpdateChunk;
21
24
use gateway_messages:: UpdateId ;
22
25
use gateway_messages:: UpdateInProgressStatus ;
23
26
use hubtools:: RawHubrisImage ;
27
+ use sha2:: Sha256 ;
24
28
use sha3:: Digest ;
25
29
use sha3:: Sha3_256 ;
26
30
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
+
27
35
pub ( crate ) struct SimSpUpdate {
28
36
/// tracks the state of any ongoing simulated update
29
37
///
@@ -38,6 +46,8 @@ pub(crate) struct SimSpUpdate {
38
46
/// data from the last completed phase1 update for each slot (exposed for
39
47
/// testing)
40
48
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 > ,
41
51
42
52
/// records whether a change to the stage0 "active slot" has been requested
43
53
pending_stage0_update : bool ,
@@ -177,6 +187,7 @@ impl SimSpUpdate {
177
187
last_sp_update_data : None ,
178
188
last_rot_update_data : None ,
179
189
last_host_phase1_update_data : BTreeMap :: new ( ) ,
190
+ phase1_hash_state : BTreeMap :: new ( ) ,
180
191
181
192
pending_stage0_update : false ,
182
193
@@ -303,6 +314,13 @@ impl SimSpUpdate {
303
314
std:: io:: Write :: write_all ( data, chunk_data)
304
315
. map_err ( |_| SpError :: UpdateIsTooLarge ) ?;
305
316
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
+
306
324
if data. position ( ) == data. get_ref ( ) . len ( ) as u64 {
307
325
let mut stolen = Cursor :: new ( Box :: default ( ) ) ;
308
326
mem:: swap ( data, & mut stolen) ;
@@ -452,6 +470,78 @@ impl SimSpUpdate {
452
470
self . last_host_phase1_update_data . get ( & slot) . cloned ( )
453
471
}
454
472
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
+
455
545
pub ( crate ) fn get_component_caboose_value (
456
546
& mut self ,
457
547
component : SpComponent ,
@@ -663,3 +753,11 @@ fn fake_fwid_compute(data: &[u8]) -> Fwid {
663
753
digest. update ( data) ;
664
754
Fwid :: Sha3_256 ( digest. finalize ( ) . into ( ) )
665
755
}
756
+
757
+ #[ derive( Debug , Clone , Copy ) ]
758
+ enum HostFlashHashState {
759
+ NeverHashed ,
760
+ HashStarted ( Instant ) ,
761
+ Hashed ( [ u8 ; 32 ] ) ,
762
+ HashInvalidated ,
763
+ }
0 commit comments