Skip to content

Commit 7c23996

Browse files
committed
wip
1 parent 49ece4b commit 7c23996

File tree

13 files changed

+283
-35
lines changed

13 files changed

+283
-35
lines changed

app/lpc55xpresso/app.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ start = true
4747
[tasks.update_server]
4848
name = "lpc55-update-server"
4949
priority = 3
50-
max-sizes = {flash = 26720, ram = 16704}
5150
stacksize = 8192
5251
start = true
5352
sections = {bootstate = "usbsram", transient_override = "override"}

drv/lpc55-sprot-server/src/handler.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,16 @@ impl<'a> Handler {
679679

680680
Ok((RspBody::State(out.map(StateRsp::LifecycleState)), None))
681681
}
682+
ReqBody::Update(UpdateReq::ComponentSwitchCancelPending {
683+
component,
684+
slot,
685+
duration,
686+
}) => {
687+
self.update.component_switch_cancel_pending(
688+
component, slot, duration,
689+
)?;
690+
Ok((RspBody::Ok, None))
691+
}
682692
}
683693
}
684694

drv/lpc55-update-server/src/main.rs

Lines changed: 94 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -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

576593
impl 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

drv/sprot-api/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,11 @@ pub enum UpdateReq {
413413
slot: SlotId,
414414
duration: SwitchDuration,
415415
},
416+
ComponentSwitchCancelPending {
417+
component: RotComponent,
418+
slot: SlotId,
419+
duration: SwitchDuration,
420+
},
416421
}
417422

418423
#[derive(Clone, Serialize, Deserialize, SerializedSize)]

drv/stm32h7-sprot-server/src/main.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,28 @@ impl<S: SpiServer> idl::InOrderSpRotImpl for ServerImpl<S> {
13451345
}
13461346
}
13471347

1348+
fn component_switch_cancel_pending(
1349+
&mut self,
1350+
_msg: &userlib::RecvMessage,
1351+
component: RotComponent,
1352+
slot: SlotId,
1353+
duration: SwitchDuration,
1354+
) -> Result<(), idol_runtime::RequestError<SprotError>> {
1355+
let body = ReqBody::Update(UpdateReq::ComponentSwitchCancelPending {
1356+
component,
1357+
slot,
1358+
duration,
1359+
});
1360+
let tx_size = Request::pack(&body, self.tx_buf);
1361+
let rsp =
1362+
self.do_send_recv_retries(tx_size, TIMEOUT_LONG, DEFAULT_ATTEMPTS)?;
1363+
if let RspBody::Ok = rsp.body? {
1364+
Ok(())
1365+
} else {
1366+
Err(SprotProtocolError::UnexpectedResponse)?
1367+
}
1368+
}
1369+
13481370
fn lifecycle_state(
13491371
&mut self,
13501372
_msg: &userlib::RecvMessage,

drv/update-api/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ pub enum UpdateError {
6767
VersionNotSupported,
6868

6969
InvalidPreferredSlotId,
70+
// AlreadyPending,
71+
// NonePending,
7072
}
7173

7274
impl From<UpdateError> for GwUpdateError {
@@ -106,6 +108,8 @@ impl From<UpdateError> for GwUpdateError {
106108
UpdateError::SignatureNotValidated => Self::SignatureNotValidated,
107109
UpdateError::VersionNotSupported => Self::VersionNotSupported,
108110
UpdateError::InvalidPreferredSlotId => Self::InvalidPreferredSlotId,
111+
// UpdateError::AlreadyPending => Self::AlreadyPending,
112+
// UpdateError::NonePending => Self::NonePending,
109113
}
110114
}
111115
}

idl/lpc55-update.idol

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,5 +212,19 @@ Interface(
212212
idempotent: true,
213213
encoding: Hubpack
214214
),
215+
"component_switch_cancel_pending": (
216+
doc: "Cancal a pending persistent or transient image switch",
217+
args: {
218+
"component": "RotComponent",
219+
"slot": "SlotId",
220+
"duration": "SwitchDuration",
221+
},
222+
reply: Result(
223+
ok: "()",
224+
err: CLike("drv_update_api::UpdateError"),
225+
),
226+
encoding: Hubpack,
227+
idempotent: true,
228+
),
215229
},
216230
)

idl/sprot.idol

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,5 +443,19 @@ Interface(
443443
encoding: Hubpack,
444444
idempotent: true,
445445
),
446+
"component_switch_cancel_pending": (
447+
doc: "Cancal a pending persistent or transient image switch",
448+
args: {
449+
"component": "RotComponent",
450+
"slot": "SlotId",
451+
"duration": "SwitchDuration",
452+
},
453+
reply: Result(
454+
ok: "()",
455+
err: Complex("SprotError"),
456+
),
457+
encoding: Hubpack,
458+
idempotent: true,
459+
),
446460
}
447461
)

task/control-plane-agent/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ enum MgsMessage {
179179
GetHostFlashHash {
180180
slot: u16,
181181
},
182+
ComponentCancelPendingActiveSlot {
183+
component: SpComponent,
184+
slot: u16,
185+
persist: bool,
186+
},
182187
}
183188

184189
// This enum does not define the actual IPC protocol - it is only used in the

task/control-plane-agent/src/mgs_common.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,52 @@ impl MgsCommon {
429429
}
430430
}
431431

432+
pub(crate) fn component_cancel_pending_active_slot(
433+
&mut self,
434+
component: SpComponent,
435+
slot: u16,
436+
persist: bool,
437+
) -> Result<(), GwSpError> {
438+
match component {
439+
SpComponent::ROT | SpComponent::STAGE0 => {
440+
let slot = slot
441+
.try_into()
442+
.map_err(|()| GwSpError::RequestUnsupportedForComponent)?;
443+
let duration = if persist {
444+
SwitchDuration::Forever
445+
} else {
446+
SwitchDuration::Once
447+
};
448+
self.sprot.component_switch_cancel_pending(
449+
if component == SpComponent::ROT {
450+
SpRotComponent::Hubris
451+
} else {
452+
SpRotComponent::Stage0
453+
},
454+
slot,
455+
duration,
456+
)?;
457+
Ok(())
458+
}
459+
460+
SpComponent::SP_ITSELF => {
461+
let slot = slot
462+
.try_into()
463+
.map_err(|()| GwSpError::RequestUnsupportedForComponent)?;
464+
if !persist {
465+
// We have no mechanism to temporarily swap the banks on the SP
466+
return Err(GwSpError::RequestUnsupportedForComponent);
467+
};
468+
self.update_sp
469+
.set_pending_boot_slot(slot)
470+
.map_err(|err| GwSpError::UpdateFailed(err as u32))?;
471+
Ok(())
472+
}
473+
// Other components might also be served someday.
474+
_ => Err(GwSpError::RequestUnsupportedForComponent),
475+
}
476+
}
477+
432478
pub(crate) fn read_sensor(
433479
&mut self,
434480
req: SensorRequest,

0 commit comments

Comments
 (0)