@@ -18,6 +18,7 @@ use drv_lpc55_update_api::{
1818 SlotId , SwitchDuration , UpdateTarget , VersionedRotBootInfo ,
1919} ;
2020use drv_update_api:: UpdateError ;
21+ use hex_literal:: hex;
2122use idol_runtime:: {
2223 ClientError , Leased , LenLimit , NotificationHandler , RequestError , R , W ,
2324} ;
@@ -40,6 +41,10 @@ const PAGE_SIZE: u32 = BYTES_PER_FLASH_PAGE as u32;
4041#[ link_section = ".bootstate" ]
4142static BOOTSTATE : MaybeUninit < [ u8 ; 0x1000 ] > = MaybeUninit :: uninit ( ) ;
4243
44+ #[ used]
45+ #[ link_section = ".transient_override" ]
46+ static mut TRANSIENT_OVERRIDE : MaybeUninit < [ u8 ; 32 ] > = MaybeUninit :: uninit ( ) ;
47+
4348#[ derive( Copy , Clone , PartialEq ) ]
4449enum UpdateState {
4550 NoUpdate ,
@@ -503,6 +508,12 @@ impl idl::InOrderUpdateImpl for ServerImpl<'_> {
503508 ringbuf_entry ! ( Trace :: State ( self . state) ) ;
504509 self . next_block = None ;
505510 self . fw_cache . fill ( 0 ) ;
511+ // The sequence: [update, set transient preference, update] is legal.
512+ // Clear any stale transient preference before update.
513+ // Stage0 doesn't support transient override.
514+ if component == RotComponent :: Hubris {
515+ set_hubris_transient_override ( None ) ;
516+ }
506517 Ok ( ( ) )
507518 }
508519
@@ -907,8 +918,11 @@ impl ServerImpl<'_> {
907918 ) -> Result < ( ) , RequestError < UpdateError > > {
908919 match duration {
909920 SwitchDuration :: Once => {
910- // TODO deposit command token into buffer
911- return Err ( UpdateError :: NotImplemented . into ( ) ) ;
921+ // TODO check Rollback policy vs epoch before activating.
922+ // TODO: prep-image-update should clear transient selection.
923+ // e.g. update, activate, update, reboot should not have
924+ // transient boot set.
925+ set_hubris_transient_override ( Some ( slot) ) ;
912926 }
913927 SwitchDuration :: Forever => {
914928 // Locate and return the authoritative CFPA flash word number
@@ -1337,6 +1351,35 @@ fn bootstate() -> Result<RotBootStateV2, HandoffDataLoadError> {
13371351 RotBootStateV2 :: load_from_addr ( addr)
13381352}
13391353
1354+ fn set_transient_override ( preference : [ u8 ; 32 ] ) {
1355+ // Safety: Data is consumed by Bootleby on next boot.
1356+ // There are no concurrent writers possible.
1357+ // Calling this function multiple times is ok.
1358+ // Bootleby is careful to vet contents before acting.
1359+ unsafe {
1360+ TRANSIENT_OVERRIDE . write ( preference) ;
1361+ }
1362+ }
1363+
1364+ pub fn set_hubris_transient_override ( bank : Option < SlotId > ) {
1365+ // Preference constants are taken from bootleby:src/lib.rs
1366+ const PREFER_SLOT_A : [ u8 ; 32 ] = hex ! (
1367+ "edb23f2e9b399c3d57695262f29615910ed10c8d9b261bfc2076b8c16c84f66d"
1368+ ) ;
1369+ const PREFER_SLOT_B : [ u8 ; 32 ] = hex ! (
1370+ "70ed2914e6fdeeebbb02763b96da9faa0160b7fc887425f4d45547071d0ce4ba"
1371+ ) ;
1372+ // Bootleby writes all zeros after reading. We write all ones to reset.
1373+ const PREFER_NOTHING : [ u8 ; 32 ] = [ 0xffu8 ; 32 ] ;
1374+
1375+ match bank {
1376+ // Do we need a value that says we were here and cleared?
1377+ None => set_transient_override ( PREFER_NOTHING ) ,
1378+ Some ( SlotId :: A ) => set_transient_override ( PREFER_SLOT_A ) ,
1379+ Some ( SlotId :: B ) => set_transient_override ( PREFER_SLOT_B ) ,
1380+ }
1381+ }
1382+
13401383fn round_up_to_flash_page ( offset : u32 ) -> Option < u32 > {
13411384 offset. checked_next_multiple_of ( BYTES_PER_FLASH_PAGE as u32 )
13421385}
0 commit comments