@@ -571,6 +571,23 @@ impl idl::InOrderUpdateImpl for ServerImpl<'_> {
571571 }
572572 }
573573 }
574+
575+ fn component_switch_cancel_pending (
576+ & mut self ,
577+ _: & userlib:: RecvMessage ,
578+ component : RotComponent ,
579+ slot : SlotId ,
580+ duration : SwitchDuration ,
581+ ) -> Result < ( ) , RequestError < UpdateError > > {
582+ match component {
583+ RotComponent :: Hubris => {
584+ self . switch_default_hubris_image_cancel_pending ( slot, duration)
585+ }
586+ RotComponent :: Stage0 => {
587+ Err ( UpdateError :: InvalidSlotIdForOperation . into ( ) )
588+ }
589+ }
590+ }
574591}
575592
576593impl NotificationHandler for ServerImpl < ' _ > {
@@ -615,7 +632,7 @@ impl ServerImpl<'_> {
615632 Ok ( val)
616633 }
617634
618- // Return the persistent and transient boot preferences
635+ // Return the persistent, pending_persistent, and transient boot preferences
619636 fn boot_preferences (
620637 & mut self ,
621638 ) -> Result < ( SlotId , Option < SlotId > , Option < SlotId > ) , UpdateError > {
@@ -946,33 +963,24 @@ impl ServerImpl<'_> {
946963 duration : SwitchDuration ,
947964 ) -> Result < ( ) , RequestError < UpdateError > > {
948965 // Note: Rollback policy will be checking epoch values before activating.
966+
949967 match duration {
950968 SwitchDuration :: Once => {
951- // Get the currently active slot.
952- let active_slot = match bootstate ( )
953- . map_err ( |_| UpdateError :: MissingHandoffData ) ?
954- . active
955- {
956- stage0_handoff:: RotSlot :: A => SlotId :: A ,
957- stage0_handoff:: RotSlot :: B => SlotId :: B ,
958- } ;
959-
960- // If the requested slot is the same as the active slot,
961- // treat this as a request to CLEAR the transient override.
962- //
963- // If we did allow setting the active slot as the transient
964- // preference, then we get this confusing 2-boot scenario when
965- // pending persistent preference is also used:
966- // the next reset returns us to the original image and the 2nd
967- // reset has us running the new/alternate image.
968- if active_slot == slot {
969- set_hubris_transient_override ( None ) ;
969+ if get_hubris_transient_override ( ) . is_some ( ) {
970+ // Maybe something is out-of-sync in the control plane?
971+ // An error returned here can help narrow that down up there.
972+ // The caller must clear the existing pending preference.
973+ return Err ( UpdateError :: AlreadyPending . into ( ) ) ;
970974 } else {
971- // Otherwise, set the preference as requested.
972975 set_hubris_transient_override ( Some ( slot) ) ;
973976 }
974977 }
975978 SwitchDuration :: Forever => {
979+ // Caller must first cancel pending persistent state if present.
980+ if let ( _, Some ( _) , _) = self . boot_preferences ( ) ? {
981+ return Err ( UpdateError :: AlreadyPending . into ( ) ) ;
982+ }
983+
976984 // Locate and return the authoritative CFPA flash word number
977985 // and the CFPA version for that flash number.
978986 //
@@ -1002,19 +1010,6 @@ impl ServerImpl<'_> {
10021010 let ( cfpa_word_number, _) =
10031011 self . cfpa_word_number_and_version ( CfpaPage :: Active ) ?;
10041012
1005- // TODO: Hubris issue #2093: If there is a pending update in the
1006- // scratch page, it is not being considered.
1007- // Consider:
1008- // - A is active
1009- // - persistent switch to B without reset
1010- // - scratch page is updated
1011- // - return Ok(())
1012- // - persistent switch to A without reset
1013- // - A is already active, no work performed
1014- // - return Ok(())
1015- // - reset
1016- // - B is active
1017-
10181013 // Read current CFPA contents.
10191014 let mut cfpa = [ [ 0u32 ; 4 ] ; 512 / 16 ] ;
10201015 indirect_flash_read_words (
@@ -1102,6 +1097,71 @@ impl ServerImpl<'_> {
11021097
11031098 Ok ( ( ) )
11041099 }
1100+
1101+ /// Cancel pending transient or persistent Hubris image selection.
1102+ fn switch_default_hubris_image_cancel_pending (
1103+ & mut self ,
1104+ slot : SlotId ,
1105+ duration : SwitchDuration ,
1106+ ) -> Result < ( ) , RequestError < UpdateError > > {
1107+ // Note: Rollback policy will be checking epoch values before activating.
1108+ match duration {
1109+ SwitchDuration :: Once => {
1110+ // Is there a pending transient boot selection?
1111+ // If it matches the one being cancelled
1112+ if let Some ( transient_selection) =
1113+ get_hubris_transient_override ( )
1114+ {
1115+ if transient_selection == slot {
1116+ set_hubris_transient_override ( None ) ;
1117+ Ok ( ( ) )
1118+ } else {
1119+ Err ( UpdateError :: InvalidPreferredSlotId . into ( ) )
1120+ }
1121+ } else {
1122+ Err ( UpdateError :: NonePending . into ( ) )
1123+ }
1124+ }
1125+ SwitchDuration :: Forever => {
1126+ // Note: Scratch page data does not have to be preserved unless/until we have
1127+ // multiple fields with pending updates. Right now, we only implement the
1128+ // pending_persistent bit. IMAGE_KEY_REVOKE and RKTH_REVOKE
1129+ // fields are not yet managed.
1130+
1131+ if let ( _, Some ( pending_persistent) , _) =
1132+ self . boot_preferences ( ) ?
1133+ {
1134+ if slot == pending_persistent {
1135+ // Cancel the CFPA update by erase/writing zeros to the
1136+ // scratch page.
1137+ // As a result, the scratch page verison will never be
1138+ // higher than the active page version.
1139+ // Also, we won't fix the CRC so it won't be deemed
1140+ // valid by the boot ROM in any case.
1141+ let cfpa = [ [ 0u32 ; 4 ] ; 512 / 16 ] ;
1142+ let cfpa_bytes: & [ u8 ] = cfpa. as_bytes ( ) ;
1143+ let cfpa_bytes: & [ u8 ; BLOCK_SIZE_BYTES ] =
1144+ cfpa_bytes. try_into ( ) . unwrap_lite ( ) ;
1145+ self . flash
1146+ . write_page (
1147+ CFPA_SCRATCH_FLASH_ADDR ,
1148+ cfpa_bytes,
1149+ wait_for_flash_interrupt,
1150+ )
1151+ . map_err ( |_| UpdateError :: FlashError . into ( ) )
1152+ } else {
1153+ // Slot mismatch.
1154+ // Fail assuming that the control plane was working with stale information.
1155+ Err ( UpdateError :: InvalidPreferredSlotId . into ( ) )
1156+ }
1157+ } else {
1158+ // No pending persistent prefereice.
1159+ // Fail assuming that the control plane was working with stale information.
1160+ Err ( UpdateError :: NonePending . into ( ) )
1161+ }
1162+ }
1163+ }
1164+ }
11051165}
11061166
11071167// Return the preferred slot to boot from for a given CFPA boot selection
0 commit comments