Skip to content

Commit d601eaf

Browse files
authored
Fix version translation for VersionedRotBootInfo (#2186)
Translate MGS 1-based versions to RoT 0-based versions for the SP/RoT `versioned_rot_boot_info()` call and clamp the version to be the lesser of the MGS's and SP's `RotBootInfo::HIGHEST_KNOWN_VERSION`. The version argument of the VersionedRotBootInfo message was being used directly for the sprot `versioned_rot_boot_info` call. These are actually two different name spaces. This change helps when an upcoming PR introduces new MGS and RoT varients to support the `BootDecisionLog`. Without this change, a new MGS and RoT running against an old SP will have some cases where an MGS request is understood by the RoT but the RoT response cannot be deserialized by the SP and results in sending an error to the MGS. Issue #2185
1 parent 10301be commit d601eaf

File tree

2 files changed

+86
-28
lines changed

2 files changed

+86
-28
lines changed

drv/lpc55-update-api/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ pub enum VersionedRotBootInfo {
161161
V1(RotBootInfo),
162162
V2(RotBootInfoV2),
163163
}
164+
impl VersionedRotBootInfo {
165+
pub const HIGHEST_KNOWN_VERSION: u8 = 2;
166+
}
164167

165168
#[derive(Clone, Copy, Serialize, Deserialize, SerializedSize)]
166169
pub enum RotPage {

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

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use drv_sprot_api::{
1313
CabooseOrSprotError,
1414
Fwid as SpFwid,
1515
ImageError as SpImageError,
16-
ImageVersion as SpImageVersion,
1716
RotBootInfo as SpRotBootInfo,
1817
RotBootInfoV2 as SpRotBootInfoV2,
1918
RotComponent as SpRotComponent,
@@ -612,6 +611,25 @@ impl MgsCommon {
612611
Ok(())
613612
}
614613

614+
/// Returns the `GwRotBootInfo` version requested by MGS, or falls back to
615+
/// the highest supported version that does not exceed the request.
616+
///
617+
/// This "best-effort" behavior simplifies the client's logic. Later
618+
/// response versions are improvements and the control plane benefits from
619+
/// the most complete information for update planning. We provide the best
620+
/// available data directly rather than forcing the client to handle errors
621+
/// and search for a supported version. Callers that require strict
622+
/// versioning can treat version mismatches as an error.
623+
///
624+
/// The supported versions are constrained by this SP and the RoT firmware.
625+
/// Some version skew between the control plane (MGS), SP, and RoT is
626+
/// expected.
627+
/// Temporary version skew is **unavoidable** during normal system updates
628+
/// but can also result from update failures or they can occur during
629+
/// development.
630+
/// The fallback logic is designed to handle these mismatches gracefully.
631+
/// Introducing a new `GwRotBootInfo` variant requires care, as an
632+
/// unhandled mismatch can break the update process.
615633
pub(crate) fn versioned_rot_boot_info(
616634
&mut self,
617635
version: u8,
@@ -620,17 +638,66 @@ impl MgsCommon {
620638
version
621639
}));
622640

623-
match self.sprot.versioned_rot_boot_info(version)? {
624-
SpVersionedRotBootInfo::V1(v1) => {
625-
Ok(GwRotBootInfo::V1(MgsRotState::from(v1).0))
641+
// For full context on the version mapping, see the RoT's
642+
// `versioned_rot_boot_info` function in `drv/lpc55-update-server`
643+
// and the corresponding MGS data structures.
644+
645+
// Ensure that the version mapping logic below is
646+
// intentionally reviewed whenever a new `GwRotBootInfo` version
647+
// or SpVersionedRotBootInfo variant is added.
648+
// If these assertions fail, both the `match version` mapping and the
649+
// `match self.sprot...` response construction must be updated.
650+
651+
// Force update of this code if new MGS variants are introduced
652+
const _HIGHEST_KNOWN_MGS_VERSION: u8 = 3;
653+
static_assertions::const_assert_eq!(
654+
GwRotBootInfo::HIGHEST_KNOWN_VERSION,
655+
_HIGHEST_KNOWN_MGS_VERSION
656+
);
657+
658+
// Force update of this code if new ROT variants are introduced
659+
const HIGHEST_KNOWN_ROT_VERSION: u8 = 2;
660+
static_assertions::const_assert_eq!(
661+
SpVersionedRotBootInfo::HIGHEST_KNOWN_VERSION,
662+
HIGHEST_KNOWN_ROT_VERSION
663+
);
664+
665+
// Map the MGS RotBootInfo 1-based versions to RoT 0-based versions.
666+
let rot_version = match version {
667+
// There is no version -1 in the RoT version number scheme.
668+
0 => return Err(GwSpError::RequestUnsupportedForComponent),
669+
670+
// See Issue #2193: To maintain compatibility with older systems,
671+
// we intentionally preserve a legacy bug where MGS V1 was passed
672+
// unaltered to the RoT instead of being mapped to RoT V0.
673+
// 1 => 0, // This is the correct mapping.
674+
1 => 1, // This is the bug-compatible mapping
675+
676+
// The remaining mappings are consistent with the design.
677+
2 => 1,
678+
3 => 2,
679+
// Clamp the version to the highest known by the SP.
680+
_ => HIGHEST_KNOWN_ROT_VERSION,
681+
};
682+
683+
match self.sprot.versioned_rot_boot_info(rot_version)? {
684+
SpVersionedRotBootInfo::V1(rot_v1) => {
685+
if version == 1 {
686+
// The MGS request is for a deprecated RoT response.
687+
// Maintain bug compatibility until issue #2193 is resolved.
688+
// Construct an MGS V1 from the more recent RoT V1 struct.
689+
Ok(GwRotBootInfo::V1(MgsRotState::from(rot_v1).0))
690+
} else {
691+
// The RoT V1 response corresponds to the MGS V2 or is the
692+
// highest that the RoT could provide.
693+
Ok(GwRotBootInfo::V2(MgsRotStateV2::from(rot_v1).0))
694+
}
695+
}
696+
// The RoT V2 response corresponds to the MGS V3 or is the highest
697+
// version the RoT could provide.
698+
SpVersionedRotBootInfo::V2(rot_v2) => {
699+
Ok(GwRotBootInfo::V3(MgsRotStateV3::from(rot_v2).0))
626700
}
627-
SpVersionedRotBootInfo::V2(v2) => match version {
628-
2 => Ok(GwRotBootInfo::V2(MgsRotStateV2::from(v2).0)),
629-
// RoT's V2 is MGS V3 and the highest version that we can offer today.
630-
_ => Ok(GwRotBootInfo::V3(MgsRotStateV3::from(v2).0)),
631-
},
632-
// New variants that this code doesn't know about yet will
633-
// result in a deserialization error.
634701
}
635702
}
636703

@@ -693,6 +760,7 @@ impl From<SpSlotId> for MgsRotSlotId {
693760
}
694761
}
695762

763+
// Note: this struct and impl go away when issue #2193 is resolved.
696764
struct MgsRotState(GwRotState);
697765

698766
impl From<SpRotBootInfo> for MgsRotState {
@@ -703,21 +771,19 @@ impl From<SpRotBootInfo> for MgsRotState {
703771
active: MgsRotSlotId::from(v1.active).0,
704772
slot_a: v1.slot_a_sha3_256_digest.map(|digest| {
705773
GwRotImageDetails {
706-
version: MgsImageVersion::from(SpImageVersion {
774+
version: GwImageVersion {
707775
version: 0,
708776
epoch: 0,
709-
})
710-
.0,
777+
},
711778
digest,
712779
}
713780
}),
714781
slot_b: v1.slot_b_sha3_256_digest.map(|digest| {
715782
GwRotImageDetails {
716-
version: MgsImageVersion::from(SpImageVersion {
783+
version: GwImageVersion {
717784
version: 0,
718785
epoch: 0,
719-
})
720-
.0,
786+
},
721787
digest,
722788
}
723789
}),
@@ -781,17 +847,6 @@ impl From<SpRotBootInfoV2> for MgsRotStateV2 {
781847
}
782848
}
783849

784-
struct MgsImageVersion(GwImageVersion);
785-
786-
impl From<SpImageVersion> for MgsImageVersion {
787-
fn from(iv: SpImageVersion) -> Self {
788-
MgsImageVersion(GwImageVersion {
789-
version: iv.version,
790-
epoch: iv.epoch,
791-
})
792-
}
793-
}
794-
795850
struct MgsRotStateV3(GwRotStateV3);
796851

797852
impl From<SpRotBootInfoV2> for MgsRotStateV3 {

0 commit comments

Comments
 (0)