Skip to content

Commit 7634f1f

Browse files
committed
Implement ComponentCancelPendingActiveSlot
This message can be used in favor of resetting the RoT when recovering from a failed or abandoned update. A RoT transient or persistent Hubris activation has a pending phase that is resolved on next boot. Clearing the pending activation allows a new update flow to proceed without resetting the RoT, or with a reset, allows the update flow to proceed without switching to a different image (assuming a properly signed alternate image).
1 parent 91c9d62 commit 7634f1f

File tree

9 files changed

+138
-3
lines changed

9 files changed

+138
-3
lines changed

faux-mgs/src/main.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,20 +236,33 @@ enum Command {
236236
long,
237237
value_name = "SLOT",
238238
requires = "switch_duration",
239+
group = "action",
240+
conflicts_with_all = ["cancel_pending"],
239241
help = "set the active slot"
240242
)]
241243
set: Option<u16>,
244+
/// Cancel a pending `set` operation.
245+
/// Used to avoid a reset when recoverying from a failed or abandoned update.
242246
#[clap(
243247
short,
244248
long,
245-
requires = "set",
249+
value_name = "SLOT",
250+
requires = "switch_duration",
251+
group = "action",
252+
conflicts_with_all = ["set"]
253+
)]
254+
cancel_pending: Option<u16>,
255+
#[clap(
256+
short,
257+
long,
258+
requires = "action",
246259
group = "switch_duration",
247260
help = "persist the active slot to non-volatile memory"
248261
)]
249262
persist: bool,
250263
/// Only valid with component "rot":
251264
/// Prefer the specified slot on the next soft reset.
252-
#[clap(short, long, requires = "set", group = "switch_duration")]
265+
#[clap(short, long, requires = "action", group = "switch_duration")]
253266
transient: bool,
254267
},
255268

@@ -1264,7 +1277,13 @@ async fn run_command(
12641277
]))
12651278
}
12661279
}
1267-
Command::ComponentActiveSlot { component, set, persist, transient } => {
1280+
Command::ComponentActiveSlot {
1281+
component,
1282+
set,
1283+
persist,
1284+
transient,
1285+
cancel_pending,
1286+
} => {
12681287
if transient && component != SpComponent::ROT {
12691288
bail!("The --transient (-t) flag is only allowed for the 'rot' component, not for {component}");
12701289
} else if let Some(slot) = set {
@@ -1276,6 +1295,20 @@ async fn run_command(
12761295
"set active slot for {component:?} to {slot}"
12771296
)]))
12781297
}
1298+
} else if let Some(slot) = cancel_pending {
1299+
sp.cancel_pending_component_active_slot(
1300+
component, slot, persist,
1301+
)
1302+
.await?;
1303+
if json {
1304+
Ok(Output::Json(
1305+
json!({ "ack": "cancel_pending", "slot": slot }),
1306+
))
1307+
} else {
1308+
Ok(Output::Lines(vec![format!(
1309+
"cancel pending active slot for {component:?} as {slot}"
1310+
)]))
1311+
}
12791312
} else {
12801313
let slot = sp.component_active_slot(component).await?;
12811314
info!(log, "active slot for {component:?}: {slot}");

gateway-messages/src/mgs_to_sp.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,16 @@ pub enum MgsRequest {
236236
GetHostFlashHash {
237237
slot: u16,
238238
},
239+
240+
/// Cancel a pending slot activation for components with multiple slots that
241+
/// have separate `pending` and `active` states.
242+
/// This is useful when recovering from a failed or abandoned update without incurring an
243+
/// additional reset or other activation step.
244+
ComponentCancelPendingActiveSlot {
245+
component: SpComponent,
246+
slot: u16,
247+
persist: bool,
248+
},
239249
}
240250

241251
#[derive(

gateway-messages/src/sp_impl.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,13 @@ pub trait SpHandler {
417417
fn start_host_flash_hash(&mut self, slot: u16) -> Result<(), SpError>;
418418

419419
fn get_host_flash_hash(&mut self, slot: u16) -> Result<[u8; 32], SpError>;
420+
421+
fn component_cancel_pending_active_slot(
422+
&mut self,
423+
component: SpComponent,
424+
slot: u16,
425+
persist: bool,
426+
) -> Result<(), SpError>;
420427
}
421428

422429
/// Handle a single incoming message.
@@ -1032,6 +1039,13 @@ fn handle_mgs_request<H: SpHandler>(
10321039
MgsRequest::GetHostFlashHash { slot } => {
10331040
handler.get_host_flash_hash(slot).map(SpResponse::HostFlashHash)
10341041
}
1042+
MgsRequest::ComponentCancelPendingActiveSlot {
1043+
component,
1044+
slot,
1045+
persist,
1046+
} => handler
1047+
.component_cancel_pending_active_slot(component, slot, persist)
1048+
.map(|()| SpResponse::ComponentCancelPendingActiveSlotAck),
10351049
};
10361050

10371051
let response = match result {
@@ -1457,6 +1471,15 @@ mod tests {
14571471
) -> Result<[u8; 32], SpError> {
14581472
unimplemented!()
14591473
}
1474+
1475+
fn component_cancel_pending_active_slot(
1476+
&mut self,
1477+
_component: SpComponent,
1478+
_slot: u16,
1479+
_persist: bool,
1480+
) -> Result<(), SpError> {
1481+
unimplemented!()
1482+
}
14601483
}
14611484

14621485
#[cfg(feature = "std")]

gateway-messages/src/sp_to_mgs.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ pub enum SpResponse {
177177

178178
/// sha2-256 hash of a flash bank
179179
HostFlashHash([u8; 32]),
180+
181+
/// Cancel a pending slot activation
182+
ComponentCancelPendingActiveSlotAck,
180183
}
181184

182185
/// Identifier for one of of an SP's KSZ8463 management-network-facing ports.

gateway-messages/tests/versioning/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mod v17;
2626
mod v18;
2727
mod v19;
2828
mod v20;
29+
mod v21;
2930

3031
pub fn assert_serialized<T: Serialize + SerializedSize + std::fmt::Debug>(
3132
expected: &[u8],
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
// Copyright 2023 Oxide Computer Company
6+
7+
//! The tests in this module check that the serialized form of messages from MGS
8+
//! protocol version 2 have not changed.
9+
//!
10+
//! If a test in this module fails, _do not change the test_! This means you
11+
//! have changed, deleted, or reordered an existing message type or enum
12+
//! variant, and you should revert that change. This will remain true until we
13+
//! bump the `version::MIN` to a value higher than 2, at which point these tests
14+
//! can be removed as we will stop supporting v2.
15+
16+
use gateway_messages::MgsRequest;
17+
use gateway_messages::SpComponent;
18+
use gateway_messages::SpResponse;
19+
20+
use super::assert_serialized;
21+
22+
#[test]
23+
fn mgs_request() {
24+
let request = MgsRequest::ComponentCancelPendingActiveSlot {
25+
component: SpComponent::SP_ITSELF,
26+
slot: 0x0102,
27+
persist: true,
28+
};
29+
#[rustfmt::skip]
30+
let expected = &[
31+
50, // ComponentCancelPendingActiveSlot
32+
b's', b'p', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // SP_ITSELF
33+
2, 1, // slot
34+
1, // persist = true
35+
];
36+
assert_serialized(expected, &request);
37+
}
38+
39+
#[test]
40+
fn sp_response() {
41+
let response = SpResponse::ComponentCancelPendingActiveSlotAck;
42+
let expected = &[52];
43+
assert_serialized(expected, &response);
44+
}

gateway-sp-comms/src/single_sp.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,22 @@ impl SingleSp {
614614
})
615615
}
616616

617+
/// Cancel a pending slot activation for a particular component..
618+
pub async fn cancel_pending_component_active_slot(
619+
&self,
620+
component: SpComponent,
621+
slot: u16,
622+
persist: bool,
623+
) -> Result<()> {
624+
self.rpc(MgsRequest::ComponentCancelPendingActiveSlot {
625+
component,
626+
slot,
627+
persist,
628+
})
629+
.await
630+
.and_then(expect_component_cancel_pending_active_slot_ack)
631+
}
632+
617633
/// Request that the status of a component be cleared (e.g., resetting
618634
/// counters).
619635
pub async fn component_clear_status(

gateway-sp-comms/src/sp_response_expect.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ expect_data_fn!(BulkIgnitionState(page) -> TlvPage);
129129
expect_data_fn!(ComponentDetails(page) -> TlvPage);
130130
expect_data_fn!(Inventory(page) -> TlvPage);
131131
expect_data_fn!(BulkIgnitionLinkEvents(page) -> TlvPage);
132+
expect_fn!(ComponentCancelPendingActiveSlotAck);
132133

133134
pub(crate) fn expect_caboose_value(
134135
r: (SocketAddrV6, SpResponse, Vec<u8>),

wireshark/protofields.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ M.mgs_request.names = {
9494
[47] = "ReadHostFlash",
9595
[48] = "StartHostFlashHash",
9696
[49] = "GetHostFlashHash",
97+
[50] = "ComponentCancelPendingActiveSlot",
9798
}
9899
M.mgs_request.handlers = {
99100
[0] = "dissect_discover",
@@ -146,6 +147,7 @@ M.mgs_request.handlers = {
146147
[47] = "dissect_read_host_flash",
147148
[48] = "dissect_start_host_flash_hash",
148149
[49] = "dissect_get_host_flash_hash",
150+
[50] = "dissect_component_cancel_pending_active_slot",
149151
}
150152
M.mgs_request.field = ProtoField.uint8(
151153
"mgs.mgs_request",
@@ -330,6 +332,7 @@ M.sp_response.names = {
330332
[49] = "ReadHostFlash",
331333
[50] = "StartHostFlashHashAck",
332334
[51] = "HostFlashHash",
335+
[52] = "ComponentCancelPendingActiveSlotAck",
333336
}
334337
M.sp_response.handlers = {
335338
[0] = "dissect_discover",
@@ -384,6 +387,7 @@ M.sp_response.handlers = {
384387
[49] = "dissect_read_host_flash",
385388
[50] = "dissect_start_host_flash_hash_ack",
386389
[51] = "dissect_host_flash_hash",
390+
[52] = "dissect_component_cancel_pending_active_slot_ack",
387391
}
388392
M.sp_response.field = ProtoField.uint8(
389393
"mgs.sp_response",

0 commit comments

Comments
 (0)