Skip to content

Commit

Permalink
Bump gateway-messages and incorporate new RoT info
Browse files Browse the repository at this point in the history
The new RoT information includes details about stage0 (RoT bootloader)
  • Loading branch information
labbott committed May 29, 2024
1 parent 1fe55e9 commit 37312d6
Show file tree
Hide file tree
Showing 36 changed files with 1,828 additions and 332 deletions.
208 changes: 41 additions & 167 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ foreign-types = "0.3.2"
fs-err = "2.11.0"
futures = "0.3.30"
gateway-client = { path = "clients/gateway-client" }
gateway-messages = { git = "https://github.com/oxidecomputer/management-gateway-service", rev = "2739c18e80697aa6bc235c935176d14b4d757ee9", default-features = false, features = ["std"] }
gateway-sp-comms = { git = "https://github.com/oxidecomputer/management-gateway-service", rev = "2739c18e80697aa6bc235c935176d14b4d757ee9" }
gateway-messages = { git = "https://github.com/oxidecomputer/management-gateway-service", rev = "c85a4ca043aaa389df12aac5348d8a3feda28762", default-features = false, features = ["std"] }
gateway-sp-comms = { git = "https://github.com/oxidecomputer/management-gateway-service", rev = "c85a4ca043aaa389df12aac5348d8a3feda28762" }
gateway-test-utils = { path = "gateway-test-utils" }
gethostname = "0.4.3"
glob = "0.3.1"
Expand Down
3 changes: 3 additions & 0 deletions clients/gateway-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,14 @@ progenitor::generate_api!(
ImageVersion = { derives = [PartialEq, Eq, PartialOrd, Ord] },
RotImageDetails = { derives = [PartialEq, Eq, PartialOrd, Ord] },
RotSlot = { derives = [PartialEq, Eq, PartialOrd, Ord] },
RotImageDetails = { derives = [ PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize] },
RotImageError = { derives = [ PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize] },
RotState = { derives = [PartialEq, Eq, PartialOrd, Ord] },
SpIdentifier = { derives = [Copy, PartialEq, Hash, Eq] },
SpIgnition = { derives = [PartialEq, Eq, PartialOrd, Ord] },
SpIgnitionSystemType = { derives = [Copy, PartialEq, Eq, PartialOrd, Ord] },
SpState = { derives = [PartialEq, Eq, PartialOrd, Ord] },
ImageVersion = { derives = [ PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize] },
},
);

Expand Down
91 changes: 88 additions & 3 deletions dev-tools/omdb/src/bin/omdb/mgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,16 @@ fn show_sp_states(
RotState::CommunicationFailed { message } => {
format!("error: {}", message)
}
RotState::Enabled { active: RotSlot::A, .. } => {
RotState::V2 { active: RotSlot::A, .. } => {
"slot A".to_string()
}
RotState::Enabled { active: RotSlot::B, .. } => {
RotState::V2 { active: RotSlot::B, .. } => {
"slot B".to_string()
}
RotState::V3 { active: RotSlot::A, .. } => {
"slot A".to_string()
}
RotState::V3 { active: RotSlot::B, .. } => {
"slot B".to_string()
}
},
Expand Down Expand Up @@ -332,7 +338,7 @@ async fn show_sp_details(
RotState::CommunicationFailed { message } => {
println!(" error: {}", message);
}
RotState::Enabled {
RotState::V2 {
active,
pending_persistent_boot_preference,
persistent_boot_preference,
Expand Down Expand Up @@ -382,6 +388,85 @@ async fn show_sp_details(
},
];

let table = tabled::Table::new(rows)
.with(tabled::settings::Style::empty())
.with(tabled::settings::Padding::new(0, 1, 0, 0))
.to_string();
println!("{}", textwrap::indent(&table.to_string(), " "));
println!("");
}
RotState::V3 {
active,
pending_persistent_boot_preference,
persistent_boot_preference,
slot_a_fwid,
slot_b_fwid,
transient_boot_preference,
stage0_fwid,
stage0next_fwid,
slot_a_status,
slot_b_status,
stage0_status,
stage0next_status,
} => {
#[derive(Tabled)]
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
struct Row {
name: &'static str,
value: String,
}

let rows = vec![
Row {
name: "active slot",
value: format!("slot {:?}", active),
},
Row {
name: "persistent boot preference",
value: format!("slot {:?}", persistent_boot_preference),
},
Row {
name: "pending persistent boot preference",
value: pending_persistent_boot_preference
.map(|s| format!("slot {:?}", s))
.unwrap_or_else(|| "-".to_string()),
},
Row {
name: "transient boot preference",
value: transient_boot_preference
.map(|s| format!("slot {:?}", s))
.unwrap_or_else(|| "-".to_string()),
},
Row { name: "slot A FWID", value: slot_a_fwid.clone() },
Row { name: "slot B FWID", value: slot_b_fwid.clone() },
Row { name: "Stage0 FWID", value: stage0_fwid.clone() },
Row { name: "Stage0Next FWID", value: stage0next_fwid.clone() },
Row {
name: "Slot A status",
value: (*slot_a_status)
.map(|x| format!("error: {:?}", x))
.unwrap_or_else(|| "VALID".to_string()),
},
Row {
name: "Slot B status",
value: (*slot_b_status)
.map(|x| format!("error: {:?}", x))
.unwrap_or_else(|| "VALID".to_string()),
},
Row {
name: "Stage0 status",
value: (*stage0_status)
.map(|x| format!("error: {:?}", x))
.unwrap_or_else(|| "VALID".to_string()),
},
Row {
name: "stage0next status",
value: (*stage0next_status)
.map(|x| format!("error: {:?}", x))
.unwrap_or_else(|| "VALID".to_string()),
},
];

let table = tabled::Table::new(rows)
.with(tabled::settings::Style::empty())
.with(tabled::settings::Padding::new(0, 1, 0, 0))
Expand Down
10 changes: 5 additions & 5 deletions gateway-test-utils/configs/sp_sim_config.test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ device_id_cert_seed = "01de00000000000000000000000000000000000000000000000000000
id = "dev-0"
device = "fake-tmp-sensor"
description = "FAKE temperature sensor 1"
capabilities.bits = 0x2
capabilities = 0x2
presence = "Present"

[[simulated_sps.sidecar.components]]
id = "dev-1"
device = "fake-tmp-sensor"
description = "FAKE temperature sensor 2"
capabilities.bits = 0x2
capabilities = 0x2
presence = "Failed"

[[simulated_sps.sidecar]]
Expand All @@ -46,15 +46,15 @@ device_id_cert_seed = "01de00000000000000000000000000000000000000000000000000000
id = "sp3-host-cpu"
device = "sp3-host-cpu"
description = "FAKE host cpu"
capabilities.bits = 0
capabilities = 0
presence = "Present"
serial_console = "[::1]:0"

[[simulated_sps.gimlet.components]]
id = "dev-0"
device = "fake-tmp-sensor"
description = "FAKE temperature sensor"
capabilities.bits = 0x2
capabilities = 0x2
presence = "Failed"

[[simulated_sps.gimlet]]
Expand All @@ -68,7 +68,7 @@ device_id_cert_seed = "01de00000000000000000000000000000000000000000000000000000
id = "sp3-host-cpu"
device = "sp3-host-cpu"
description = "FAKE host cpu"
capabilities.bits = 0
capabilities = 0
presence = "Present"
serial_console = "[::1]:0"

Expand Down
107 changes: 104 additions & 3 deletions gateway/src/http_entrypoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,34 @@ pub struct SpState {
pub rot: RotState,
}

#[derive(
Debug,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Deserialize,
Serialize,
JsonSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum RotImageError {
Unchecked,
FirstPageErased,
PartiallyProgrammed,
InvalidLength,
HeaderNotProgrammed,
BootloaderTooSmall,
BadMagic,
HeaderImageSize,
UnalignedLength,
UnsupportedType,
ResetVectorNotThumb2,
ResetVector,
Signature,
}

#[derive(
Debug,
Clone,
Expand All @@ -74,7 +102,7 @@ pub struct SpState {
)]
#[serde(tag = "state", rename_all = "snake_case")]
pub enum RotState {
Enabled {
V2 {
active: RotSlot,
persistent_boot_preference: RotSlot,
pending_persistent_boot_preference: Option<RotSlot>,
Expand All @@ -85,6 +113,22 @@ pub enum RotState {
CommunicationFailed {
message: String,
},
V3 {
active: RotSlot,
persistent_boot_preference: RotSlot,
pending_persistent_boot_preference: Option<RotSlot>,
transient_boot_preference: Option<RotSlot>,

slot_a_fwid: String,
slot_b_fwid: String,
stage0_fwid: String,
stage0next_fwid: String,

slot_a_status: Option<RotImageError>,
slot_b_status: Option<RotImageError>,
stage0_status: Option<RotImageError>,
stage0next_status: Option<RotImageError>,
},
}

#[derive(
Expand Down Expand Up @@ -184,6 +228,21 @@ pub struct RotCfpa {
pub slot: RotCfpaSlot,
}

#[derive(
Debug,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Deserialize,
Serialize,
JsonSchema,
)]
pub struct GetRotBootInfoParams {
pub version: u8,
}

#[derive(
Debug,
Clone,
Expand Down Expand Up @@ -626,7 +685,12 @@ async fn sp_get(
SpCommsError::SpCommunicationFailed { sp: sp_id, err }
})?;

Ok(HttpResponseOk(state.into()))
let rot_state = sp
.rot_state(gateway_messages::RotBootInfo::HIGHEST_KNOWN_VERSION)
.await;

let final_state = SpState::from((state, rot_state));
Ok(HttpResponseOk(final_state))
}

/// Get host startup options for a sled
Expand Down Expand Up @@ -1044,7 +1108,9 @@ async fn sp_component_reset(
let component = component_from_str(&component)?;

sp.reset_component_prepare(component)
.and_then(|()| sp.reset_component_trigger(component))
// We always want to run with the watchdog when resetting as
// disabling the watchdog should be considered a debug only feature
.and_then(|()| sp.reset_component_trigger(component, false))
.await
.map_err(|err| SpCommsError::SpCommunicationFailed {
sp: sp_id,
Expand Down Expand Up @@ -1224,6 +1290,40 @@ async fn sp_rot_cfpa_get(
Ok(HttpResponseOk(RotCfpa { base64_data, slot }))
}

/// Read the RoT boot state from a root of trust
///
/// This endpoint is only valid for the `rot` component.
#[endpoint {
method = GET,
path = "/sp/{type}/{slot}/component/{component}/rot-boot-info",
}]
async fn sp_rot_boot_info(
rqctx: RequestContext<Arc<ServerContext>>,
path: Path<PathSpComponent>,
params: TypedBody<GetRotBootInfoParams>,
) -> Result<HttpResponseOk<RotState>, HttpError> {
let apictx = rqctx.context();

let PathSpComponent { sp, component } = path.into_inner();
let GetRotBootInfoParams { version } = params.into_inner();
let sp_id = sp.into();

// Ensure the caller knows they're asking for the RoT
if component_from_str(&component)? != SpComponent::ROT {
return Err(HttpError::for_bad_request(
Some("RequestUnsupportedForComponent".to_string()),
"rot_boot_info only makes sent for a RoT".into(),
));
}

let sp = apictx.mgmt_switch.sp(sp_id)?;
let state = sp.rot_state(version).await.map_err(|err| {
SpCommsError::SpCommunicationFailed { sp: sp_id, err }
})?;

Ok(HttpResponseOk(state.into()))
}

/// List SPs via Ignition
///
/// Retreive information for all SPs via the Ignition controller. This is lower
Expand Down Expand Up @@ -1600,6 +1700,7 @@ pub fn api() -> GatewayApiDescription {
api.register(sp_component_update_abort)?;
api.register(sp_rot_cmpa_get)?;
api.register(sp_rot_cfpa_get)?;
api.register(sp_rot_boot_info)?;
api.register(sp_host_phase2_progress_get)?;
api.register(sp_host_phase2_progress_delete)?;
api.register(ignition_list)?;
Expand Down
Loading

0 comments on commit 37312d6

Please sign in to comment.