diff --git a/Cargo.lock b/Cargo.lock index b5f340d180..143327cbb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,12 +1160,14 @@ dependencies = [ "ereports", "fixedstr", "gnarle", + "hubpack", "idol", "idol-runtime", "microcbor", "num-traits", "pmbus", "ringbuf", + "serde", "static-cell", "task-jefe-api", "task-packrat-api", @@ -1179,8 +1181,10 @@ name = "drv-cpu-power-state" version = "0.1.0" dependencies = [ "counters", + "hubpack", "microcbor", "num-traits", + "serde", "userlib", "zerocopy 0.8.27", "zerocopy-derive 0.8.27", @@ -1193,9 +1197,11 @@ dependencies = [ "counters", "derive-idol-err", "drv-cpu-power-state", + "hubpack", "idol", "idol-runtime", "num-traits", + "serde", "userlib", "zerocopy 0.8.27", "zerocopy-derive 0.8.27", @@ -1414,10 +1420,12 @@ dependencies = [ "drv-cpu-seq-api", "drv-spartan7-loader-api", "drv-stm32xx-sys-api", + "hubpack", "idol", "idol-runtime", "num-traits", "ringbuf", + "serde", "task-jefe-api", "task-packrat-api", "userlib", @@ -1991,9 +1999,11 @@ version = "0.1.0" dependencies = [ "drv-cpu-power-state", "drv-cpu-seq-api", + "hubpack", "idol", "idol-runtime", "num-traits", + "serde", "task-jefe-api", "userlib", "zerocopy 0.8.27", @@ -2178,6 +2188,7 @@ dependencies = [ "drv-fpga-user-api", "drv-ignition-api", "gnarle", + "hubpack", "idol", "num-derive 0.4.2", "num-traits", @@ -3131,7 +3142,7 @@ dependencies = [ [[package]] name = "gateway-messages" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/management-gateway-service#177c9c719e12896c566a1b6b5416c9bc686531d3" +source = "git+https://github.com/oxidecomputer/management-gateway-service?branch=evan%2Freport-power-state-change-reason#9399b11bed1af47ba57b67b724bc318f622ac5f8" dependencies = [ "bitflags 2.9.4", "hubpack", diff --git a/Cargo.toml b/Cargo.toml index 5c5d47e354..8c9bdb73b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -152,7 +152,7 @@ apob = { git = "https://github.com/oxidecomputer/apob", default-features = false # for the migration. attest-data = { git = "https://github.com/oxidecomputer/dice-util", default-features = false, version = "0.4.0", rev = "a0811d06c75c757a6e12c91ed6ea81fde137ba43" } dice-mfg-msgs = { git = "https://github.com/oxidecomputer/dice-util", default-features = false, version = "0.2.1", rev = "a0811d06c75c757a6e12c91ed6ea81fde137ba43" } -gateway-messages = { git = "https://github.com/oxidecomputer/management-gateway-service", default-features = false, features = ["smoltcp"] } +gateway-messages = { git = "https://github.com/oxidecomputer/management-gateway-service", branch = "evan/report-power-state-change-reason", default-features = false, features = ["smoltcp"] } gateway-ereport-messages = { git = "https://github.com/oxidecomputer/management-gateway-service", default-features = false } gimlet-inspector-protocol = { git = "https://github.com/oxidecomputer/gimlet-inspector-protocol", version = "0.1.0" } hif = { git = "https://github.com/oxidecomputer/hif", default-features = false } diff --git a/drv/cosmo-seq-server/Cargo.toml b/drv/cosmo-seq-server/Cargo.toml index 1b40955b59..19515e7725 100644 --- a/drv/cosmo-seq-server/Cargo.toml +++ b/drv/cosmo-seq-server/Cargo.toml @@ -27,9 +27,11 @@ fixedstr = { path = "../../lib/fixedstr", features = ["microcbor"] } static-cell = { path = "../../lib/static-cell" } cfg-if = { workspace = true } +hubpack = { workspace = true } idol-runtime.workspace = true num-traits = { workspace = true } pmbus = { workspace = true } +serde = { workspace = true } zerocopy = { workspace = true } zerocopy-derive = { workspace = true } diff --git a/drv/cosmo-seq-server/src/main.rs b/drv/cosmo-seq-server/src/main.rs index fe21926762..faf172c024 100644 --- a/drv/cosmo-seq-server/src/main.rs +++ b/drv/cosmo-seq-server/src/main.rs @@ -8,7 +8,8 @@ #![no_main] use drv_cpu_seq_api::{ - PowerState, SeqError as CpuSeqError, StateChangeReason, Transition, + PowerState, PowerStateWithReason, SeqError as CpuSeqError, + StateChangeReason, Transition, }; use drv_ice40_spi_program as ice40; use drv_packrat_vpd_loader::{Packrat, read_vpd_and_load_packrat}; @@ -429,6 +430,8 @@ fn init_front_fpga( #[allow(unused)] struct ServerImpl { state: PowerState, + /// The reason we transitioned to the current state + reason: StateChangeReason, /// The Hubris tick at which we transitioned to the current state. since: u64, jefe: Jefe, @@ -466,6 +469,7 @@ impl ServerImpl { ServerImpl { state: PowerState::A2, + reason: StateChangeReason::InitialPowerOn, since: now, jefe, sys: Sys::from(SYS.get_task_id()), @@ -478,8 +482,11 @@ impl ServerImpl { } } - fn get_state_impl(&self) -> PowerState { - self.state + fn get_state_impl(&self) -> PowerStateWithReason { + PowerStateWithReason { + state: self.state, + reason: self.reason, + } } /// Logs a set of state registers, returning the state machine states @@ -510,18 +517,18 @@ impl ServerImpl { fn set_state_impl( &mut self, state: PowerState, - why: StateChangeReason, + reason: StateChangeReason, ) -> Result { let now = sys_get_timer().now; ringbuf_entry!(Trace::SetState { prev: Some(self.state), next: state, - why, + why: reason, now, }); use seq_api_status::A0Sm; - match (self.get_state_impl(), state) { + match (self.get_state_impl().state, state) { (PowerState::A2, PowerState::A0) => { // Reset edge counters in the sequencer self.seq.amd_reset_fedges.set_counts(0); @@ -698,13 +705,19 @@ impl ServerImpl { _ => return Err(CpuSeqError::IllegalTransition), } - self.set_state_internal(state, now); + self.set_state_internal(state, reason, now); Ok(Transition::Changed) } /// Updates our internal `state` and the global state in `jefe` - fn set_state_internal(&mut self, state: PowerState, now: u64) { + fn set_state_internal( + &mut self, + state: PowerState, + reason: StateChangeReason, + now: u64, + ) { self.state = state; + self.reason = reason; self.since = now; self.jefe.set_state(state as u32); self.poke_timer(); @@ -970,36 +983,42 @@ impl ServerImpl { // host_sp_comms will be notified of this change and will // call back into this task to reboot the system (going to // A2 then back into A0) + let next = PowerState::A0Reset; + let reason = StateChangeReason::CpuReset; ringbuf_entry!(Trace::SetState { prev: Some(self.state), - next: PowerState::A0Reset, - why: StateChangeReason::CpuReset, + next, + why: reason, now, }); - self.set_state_internal(PowerState::A0Reset, now); + self.set_state_internal(next, reason, now); } InternalAction::NicMapo => { // Presumably we are in A0+HP, so send us back to A0 so that the // thermal loop will stop trying to talk to the NIC, and hope // the host resequences it. + let next = PowerState::A0; + let reason = StateChangeReason::NicMapo; ringbuf_entry!(Trace::SetState { prev: Some(self.state), - next: PowerState::A0, - why: StateChangeReason::NicMapo, + next, + why: reason, now, }); - self.set_state_internal(PowerState::A0, now); + self.set_state_internal(next, reason, now); } InternalAction::ThermTrip => { // This is a terminal state; we set our state to `A0Thermtrip` // but do not expect any other task to take action right now + let next = PowerState::A0Thermtrip; + let reason = StateChangeReason::Overheat; ringbuf_entry!(Trace::SetState { prev: Some(self.state), - next: PowerState::A0Thermtrip, - why: StateChangeReason::Overheat, + next, + why: reason, now, }); - self.set_state_internal(PowerState::A0Thermtrip, now); + self.set_state_internal(next, reason, now); } InternalAction::Mapo => { // This is a terminal state (for now) @@ -1036,6 +1055,14 @@ impl idl::InOrderSequencerImpl for ServerImpl { &mut self, _: &RecvMessage, ) -> Result> { + Ok(self.get_state_impl().state) + } + + fn get_state_with_reason( + &mut self, + _: &RecvMessage, + ) -> Result> + { Ok(self.get_state_impl()) } @@ -1291,7 +1318,7 @@ static HOST_CPU_REFDES: ereports::cpu::HostCpuRefdes = //////////////////////////////////////////////////////////////////////////////// mod idl { - use drv_cpu_seq_api::StateChangeReason; + use drv_cpu_seq_api::{PowerStateWithReason, StateChangeReason}; include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); } diff --git a/drv/cpu-power-state/Cargo.toml b/drv/cpu-power-state/Cargo.toml index b83d87bad0..17598a62d4 100644 --- a/drv/cpu-power-state/Cargo.toml +++ b/drv/cpu-power-state/Cargo.toml @@ -10,6 +10,8 @@ zerocopy-derive = { workspace = true } num-traits = { workspace = true } counters = { path = "../../lib/counters", features = ["derive"] } microcbor = { path = "../../lib/microcbor", optional = true } +hubpack = { workspace = true } +serde = { workspace = true } [lib] test = false diff --git a/drv/cpu-power-state/src/lib.rs b/drv/cpu-power-state/src/lib.rs index 87bf9e34f9..6beaa46d6e 100644 --- a/drv/cpu-power-state/src/lib.rs +++ b/drv/cpu-power-state/src/lib.rs @@ -7,6 +7,8 @@ #![no_std] +use hubpack::SerializedSize; +use serde::{Deserialize, Serialize}; use userlib::FromPrimitive; use zerocopy::{Immutable, IntoBytes, KnownLayout}; @@ -21,6 +23,9 @@ use zerocopy::{Immutable, IntoBytes, KnownLayout}; Immutable, KnownLayout, counters::Count, + Deserialize, + Serialize, + SerializedSize, )] #[cfg_attr(feature = "microcbor", derive(microcbor::Encode))] #[repr(u8)] diff --git a/drv/cpu-seq-api/Cargo.toml b/drv/cpu-seq-api/Cargo.toml index 23833d7664..efc50d0ab6 100644 --- a/drv/cpu-seq-api/Cargo.toml +++ b/drv/cpu-seq-api/Cargo.toml @@ -4,8 +4,10 @@ version = "0.1.0" edition = "2024" [dependencies] +hubpack.workspace = true idol-runtime = { workspace = true } num-traits.workspace = true +serde.workspace = true zerocopy.workspace = true zerocopy-derive.workspace = true diff --git a/drv/cpu-seq-api/src/lib.rs b/drv/cpu-seq-api/src/lib.rs index 118ab536d1..f11414eb88 100644 --- a/drv/cpu-seq-api/src/lib.rs +++ b/drv/cpu-seq-api/src/lib.rs @@ -8,6 +8,8 @@ use counters::Count; use derive_idol_err::IdolError; +use hubpack::SerializedSize; +use serde::{Deserialize, Serialize}; use userlib::{FromPrimitive, sys_send}; use zerocopy::{Immutable, IntoBytes, KnownLayout}; @@ -43,6 +45,9 @@ pub enum SeqError { Immutable, KnownLayout, Count, + SerializedSize, + Serialize, + Deserialize, )] #[repr(u8)] pub enum StateChangeReason { @@ -81,6 +86,24 @@ pub enum StateChangeReason { Unknown, } +#[derive( + Copy, + Clone, + Debug, + Eq, + PartialEq, + IntoBytes, + Immutable, + KnownLayout, + Deserialize, + Serialize, + SerializedSize, +)] +pub struct PowerStateWithReason { + pub state: PowerState, + pub reason: StateChangeReason, +} + /// Indicates the result of a power state transition. #[derive( Copy, diff --git a/drv/gimlet-seq-server/src/main.rs b/drv/gimlet-seq-server/src/main.rs index e4a1098eb3..8fcf42310e 100644 --- a/drv/gimlet-seq-server/src/main.rs +++ b/drv/gimlet-seq-server/src/main.rs @@ -20,7 +20,9 @@ use userlib::{ use zerocopy::IntoBytes; use crate::i2c_config::MAX_COMPONENT_ID_LEN as REFDES_LEN; -use drv_cpu_seq_api::{PowerState, SeqError, StateChangeReason, Transition}; +use drv_cpu_seq_api::{ + PowerState, PowerStateWithReason, SeqError, StateChangeReason, Transition, +}; use drv_hf_api as hf_api; use drv_i2c_api as i2c; use drv_ice40_spi_program as ice40; @@ -200,6 +202,8 @@ fn main() -> ! { struct ServerImpl { state: PowerState, + /// The reason we transitioned to the current state + reason: StateChangeReason, /// The Hubris tick at which we transitioned to the current state. since: u64, sys: sys_api::Sys, @@ -532,6 +536,7 @@ impl ServerImpl { let mut server = Self { state: PowerState::A2, + reason: StateChangeReason::InitialPowerOn, since: 0, // we have been in A2 since we booted :) sys: sys.clone(), seq, @@ -622,7 +627,12 @@ impl NotificationHandler for ServerImpl { self.seq .clear_bytes(Addr::NIC_CTRL, &[cld_rst]) .unwrap_lite(); - self.update_state_internal(PowerState::A0PlusHP, now); + // TODO what's the right reason? + self.update_state_internal( + PowerState::A0PlusHP, + StateChangeReason::Unknown, + now, + ); } (PowerState::A0PlusHP, true) => { @@ -654,8 +664,10 @@ impl NotificationHandler for ServerImpl { self.seq .set_bytes(Addr::NIC_CTRL, &[cld_rst]) .unwrap_lite(); + // TODO what's the right reason? self.update_state_internal( PowerState::A0, + StateChangeReason::Unknown, sys_get_timer().now, ); } @@ -728,17 +740,23 @@ where } impl ServerImpl { - fn update_state_internal(&mut self, state: PowerState, now: u64) { + fn update_state_internal( + &mut self, + state: PowerState, + reason: StateChangeReason, + now: u64, + ) { ringbuf_entry!(Trace::UpdateState(state)); self.since = now; self.state = state; + self.reason = reason; self.jefe.set_state(state as u32); } fn set_state_internal( &mut self, state: PowerState, - why: StateChangeReason, + reason: StateChangeReason, ) -> Result { let sys = sys_api::Sys::from(SYS.get_task_id()); @@ -746,7 +764,7 @@ impl ServerImpl { ringbuf_entry!(Trace::SetState { prev: self.state, next: state, - why, + why: reason, now }); @@ -969,7 +987,7 @@ impl ServerImpl { let now = sys_get_timer().now; ringbuf_entry!(Trace::A0((now.wrapping_sub(start)) as u16)); - self.update_state_internal(PowerState::A0, now); + self.update_state_internal(PowerState::A0, reason, now); Ok(Transition::Changed) } @@ -1015,7 +1033,11 @@ impl ServerImpl { return Err(SeqError::MuxToSPFailed); } - self.update_state_internal(PowerState::A2, sys_get_timer().now); + self.update_state_internal( + PowerState::A2, + reason, + sys_get_timer().now, + ); ringbuf_entry_v3p3_sys_a0_vout(); ringbuf_entry!(Trace::A2); @@ -1101,7 +1123,11 @@ impl ServerImpl { cpu: &HOST_CPU_REFDES, state: self.ereport_current_state(), }); - self.update_state_internal(PowerState::A0Thermtrip, now); + self.update_state_internal( + PowerState::A0Thermtrip, + StateChangeReason::Overheat, + now, + ); } } @@ -1137,7 +1163,11 @@ impl ServerImpl { let mask = pwrok_fedge | Reg::IFR::AMD_RSTN_FEDGE; self.seq.clear_bytes(Addr::IFR, &[mask]).unwrap_lite(); - self.update_state_internal(PowerState::A0Reset, now); + self.update_state_internal( + PowerState::A0Reset, + StateChangeReason::CpuReset, + now, + ); } } @@ -1175,6 +1205,17 @@ impl idl::InOrderSequencerImpl for ServerImpl { Ok(self.state) } + fn get_state_with_reason( + &mut self, + _: &RecvMessage, + ) -> Result> + { + Ok(PowerStateWithReason { + state: self.state, + reason: self.reason, + }) + } + fn set_state( &mut self, _: &RecvMessage, @@ -1660,7 +1701,7 @@ static HOST_CPU_REFDES: ereports::cpu::HostCpuRefdes = //////////////////////////////////////////////////////////////////////////////// mod idl { - use super::StateChangeReason; + use super::{PowerStateWithReason, StateChangeReason}; include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); } diff --git a/drv/grapefruit-seq-server/Cargo.toml b/drv/grapefruit-seq-server/Cargo.toml index a7f50be41b..2a5f37195f 100644 --- a/drv/grapefruit-seq-server/Cargo.toml +++ b/drv/grapefruit-seq-server/Cargo.toml @@ -16,8 +16,10 @@ userlib = { path = "../../sys/userlib", features = ["panic-messages"] } task-jefe-api = { path = "../../task/jefe-api" } cfg-if = { workspace = true } +hubpack = { workspace = true } idol-runtime.workspace = true num-traits = { workspace = true } +serde = { workspace = true } zerocopy = { workspace = true } zerocopy-derive = { workspace = true } diff --git a/drv/grapefruit-seq-server/src/main.rs b/drv/grapefruit-seq-server/src/main.rs index ea0aec9715..f922634fa2 100644 --- a/drv/grapefruit-seq-server/src/main.rs +++ b/drv/grapefruit-seq-server/src/main.rs @@ -7,7 +7,9 @@ #![no_std] #![no_main] -use drv_cpu_seq_api::{PowerState, SeqError, StateChangeReason, Transition}; +use drv_cpu_seq_api::{ + PowerState, PowerStateWithReason, SeqError, StateChangeReason, Transition, +}; use drv_spartan7_loader_api::Spartan7Loader; use drv_stm32xx_sys_api as sys_api; use idol_runtime::{NotificationHandler, RequestError}; @@ -77,6 +79,7 @@ struct ServerImpl { jefe: Jefe, sgpio: fmc_periph::sgpio::Sgpio, espi: fmc_periph::espi::Espi, + reason: StateChangeReason, } impl ServerImpl { @@ -99,6 +102,7 @@ impl ServerImpl { jefe: Jefe::from(JEFE.get_task_id()), sgpio: fmc_periph::sgpio::Sgpio::new(loader.get_token()), espi: fmc_periph::espi::Espi::new(loader.get_token()), + reason: StateChangeReason::InitialPowerOn, }; // Note that we don't use `Self::set_state_impl` here, as that will @@ -113,22 +117,27 @@ impl ServerImpl { server } - fn get_state_impl(&self) -> PowerState { + fn get_state_impl(&self) -> PowerStateWithReason { // Only we should be setting the state, and we set it to A2 on startup; // this conversion should never fail. - PowerState::from_u32(self.jefe.get_state()).unwrap_lite() + PowerStateWithReason { + state: PowerState::from_u32(self.jefe.get_state()).unwrap_lite(), + reason: self.reason, + } } fn set_state_impl( - &self, + &mut self, state: PowerState, + reason: StateChangeReason, ) -> Result { - match (self.get_state_impl(), state) { + match (self.get_state_impl().state, state) { (PowerState::A2, PowerState::A0) | (PowerState::A0, PowerState::A2) | (PowerState::A0PlusHP, PowerState::A2) | (PowerState::A0Thermtrip, PowerState::A2) => { self.jefe.set_state(state as u32); + self.reason = reason; Ok(Transition::Changed) } @@ -149,6 +158,14 @@ impl idl::InOrderSequencerImpl for ServerImpl { &mut self, _: &RecvMessage, ) -> Result> { + Ok(self.get_state_impl().state) + } + + fn get_state_with_reason( + &mut self, + _: &RecvMessage, + ) -> Result> + { Ok(self.get_state_impl()) } @@ -157,16 +174,16 @@ impl idl::InOrderSequencerImpl for ServerImpl { _: &RecvMessage, state: PowerState, ) -> Result> { - Ok(self.set_state_impl(state)?) + Ok(self.set_state_impl(state, StateChangeReason::Other)?) } fn set_state_with_reason( &mut self, _: &RecvMessage, state: PowerState, - _: StateChangeReason, + reason: StateChangeReason, ) -> Result> { - Ok(self.set_state_impl(state)?) + Ok(self.set_state_impl(state, reason)?) } fn send_hardware_nmi( @@ -264,7 +281,8 @@ impl NotificationHandler for ServerImpl { } mod idl { - use drv_cpu_seq_api::StateChangeReason; + use drv_cpu_seq_api::{PowerStateWithReason, StateChangeReason}; + include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); } diff --git a/drv/mock-gimlet-seq-server/Cargo.toml b/drv/mock-gimlet-seq-server/Cargo.toml index 88403bd2c8..c185ccc111 100644 --- a/drv/mock-gimlet-seq-server/Cargo.toml +++ b/drv/mock-gimlet-seq-server/Cargo.toml @@ -4,8 +4,10 @@ version = "0.1.0" edition = "2024" [dependencies] +hubpack = { workspace = true } idol-runtime = { workspace = true } num-traits = { workspace = true } +serde = { workspace = true } zerocopy = { workspace = true } zerocopy-derive = { workspace = true } diff --git a/drv/mock-gimlet-seq-server/src/main.rs b/drv/mock-gimlet-seq-server/src/main.rs index 42417b9e6b..279cf8b5be 100644 --- a/drv/mock-gimlet-seq-server/src/main.rs +++ b/drv/mock-gimlet-seq-server/src/main.rs @@ -7,7 +7,9 @@ #![no_std] #![no_main] -use drv_cpu_seq_api::{PowerState, SeqError, StateChangeReason, Transition}; +use drv_cpu_seq_api::{ + PowerState, PowerStateWithReason, SeqError, StateChangeReason, Transition, +}; use idol_runtime::{NotificationHandler, RequestError}; use task_jefe_api::Jefe; use userlib::{FromPrimitive, RecvMessage, UnwrapLite}; @@ -26,6 +28,7 @@ fn main() -> ! { struct ServerImpl { jefe: Jefe, + reason: StateChangeReason, } impl ServerImpl { @@ -34,25 +37,33 @@ impl ServerImpl { // first attempt to get the current power state from `jefe`, and we // haven't set it yet! jefe.set_state(PowerState::A2 as u32); - Self { jefe } + Self { + jefe, + reason: StateChangeReason::InitialPowerOn, + } } - fn get_state_impl(&self) -> PowerState { + fn get_state_impl(&self) -> PowerStateWithReason { // Only we should be setting the state, and we set it to A2 on startup; // this conversion should never fail. - PowerState::from_u32(self.jefe.get_state()).unwrap_lite() + PowerStateWithReason { + state: PowerState::from_u32(self.jefe.get_state()).unwrap_lite(), + reason: self.reason, + } } fn set_state_impl( - &self, + &mut self, state: PowerState, + reason: StateChangeReason, ) -> Result { - match (self.get_state_impl(), state) { + match (self.get_state_impl().state, state) { (PowerState::A2, PowerState::A0) | (PowerState::A0, PowerState::A2) | (PowerState::A0PlusHP, PowerState::A2) | (PowerState::A0Thermtrip, PowerState::A2) => { self.jefe.set_state(state as u32); + self.reason = reason; Ok(Transition::Changed) } @@ -70,6 +81,14 @@ impl idl::InOrderSequencerImpl for ServerImpl { &mut self, _: &RecvMessage, ) -> Result> { + Ok(self.get_state_impl().state) + } + + fn get_state_with_reason( + &mut self, + _: &RecvMessage, + ) -> Result> + { Ok(self.get_state_impl()) } @@ -78,16 +97,16 @@ impl idl::InOrderSequencerImpl for ServerImpl { _: &RecvMessage, state: PowerState, ) -> Result> { - Ok(self.set_state_impl(state)?) + Ok(self.set_state_impl(state, StateChangeReason::Other)?) } fn set_state_with_reason( &mut self, _: &RecvMessage, state: PowerState, - _: StateChangeReason, + reason: StateChangeReason, ) -> Result> { - Ok(self.set_state_impl(state)?) + Ok(self.set_state_impl(state, reason)?) } fn send_hardware_nmi( @@ -173,7 +192,7 @@ impl NotificationHandler for ServerImpl { } mod idl { - use super::StateChangeReason; + use drv_cpu_seq_api::{PowerStateWithReason, StateChangeReason}; include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); } diff --git a/drv/sidecar-mainboard-controller/Cargo.toml b/drv/sidecar-mainboard-controller/Cargo.toml index 9fa81d05be..58bc9e46f6 100644 --- a/drv/sidecar-mainboard-controller/Cargo.toml +++ b/drv/sidecar-mainboard-controller/Cargo.toml @@ -7,8 +7,10 @@ edition = "2024" bitfield = { workspace = true } cfg-if = { workspace = true } derive_more = { workspace = true } +hubpack = { workspace = true } num-derive = { workspace = true } num-traits = { workspace = true } +serde = { workspace = true } zerocopy = { workspace = true } zerocopy-derive = { workspace = true } diff --git a/drv/sidecar-mainboard-controller/src/tofino2.rs b/drv/sidecar-mainboard-controller/src/tofino2.rs index 8627b5fde4..c9bfc6eb21 100644 --- a/drv/sidecar-mainboard-controller/src/tofino2.rs +++ b/drv/sidecar-mainboard-controller/src/tofino2.rs @@ -7,6 +7,8 @@ use bitfield::bitfield; use derive_more::{From, Into}; use drv_fpga_api::{FpgaError, FpgaUserDesign, WriteOp}; use drv_fpga_user_api::power_rail::*; +use hubpack::SerializedSize; +use serde::{Deserialize, Serialize}; use userlib::FromPrimitive; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -21,6 +23,9 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; IntoBytes, Immutable, KnownLayout, + Deserialize, + Serialize, + SerializedSize, )] #[repr(u8)] pub enum TofinoSeqState { diff --git a/drv/sidecar-seq-api/src/lib.rs b/drv/sidecar-seq-api/src/lib.rs index f752ac9359..48b81d7669 100644 --- a/drv/sidecar-seq-api/src/lib.rs +++ b/drv/sidecar-seq-api/src/lib.rs @@ -59,6 +59,9 @@ impl From for SeqError { IntoBytes, Immutable, KnownLayout, + Deserialize, + Serialize, + SerializedSize, )] #[repr(u8)] pub enum TofinoSequencerPolicy { @@ -67,6 +70,57 @@ pub enum TofinoSequencerPolicy { RestartOnFault = 2, } +#[derive( + Copy, + Clone, + Debug, + FromPrimitive, + Eq, + PartialEq, + IntoBytes, + Immutable, + KnownLayout, + Deserialize, + Serialize, + SerializedSize, +)] +#[repr(u8)] +pub enum PolicyChangeReason { + /// No reason was provided. + /// + /// This indicates a legacy caller of `Sequencer.set_tofino_seq_policy`, + /// rather than `Sequencer.set_tofino_seq_policy_with_reason`. All + /// Hubris-internal callers should use `set_tofino_seq_policy_with_reason`, + /// so this variant generally indicates that the + /// `Sequencer.set_tofino_seq_policy` IPC is being called via Hiffy. + Other = 1, + /// The system has just received power and has entered this state as part of + /// its normal startup process. + InitialPowerOn, + /// A power state change was requested by the control plane. + ControlPlane, + /// The system powered off because a component has overheated. + Overheat, +} + +#[derive( + Copy, + Clone, + Debug, + Eq, + PartialEq, + IntoBytes, + Immutable, + KnownLayout, + Deserialize, + Serialize, + SerializedSize, +)] +pub struct TofinoSeqStateWithReason { + pub state: TofinoSeqState, + pub reason: PolicyChangeReason, +} + #[derive( Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, SerializedSize, )] diff --git a/drv/sidecar-seq-server/src/main.rs b/drv/sidecar-seq-server/src/main.rs index c6e5cb90be..f7fdee8576 100644 --- a/drv/sidecar-seq-server/src/main.rs +++ b/drv/sidecar-seq-server/src/main.rs @@ -21,7 +21,8 @@ use drv_sidecar_mainboard_controller::fan_modules::*; use drv_sidecar_mainboard_controller::front_io::*; use drv_sidecar_mainboard_controller::tofino2::*; use drv_sidecar_seq_api::{ - FanModuleIndex, FanModulePresence, SeqError, TofinoSequencerPolicy, + FanModuleIndex, FanModulePresence, PolicyChangeReason, SeqError, + TofinoSeqStateWithReason, TofinoSequencerPolicy, }; use drv_stm32xx_sys_api as sys_api; use fixedstr::FixedStr; @@ -64,7 +65,7 @@ enum Trace { ClockConfigurationError(usize, ResponseCode), ClockConfigurationComplete, TofinoSequencerError(SeqError), - TofinoSequencerPolicyUpdate(TofinoSequencerPolicy), + TofinoSequencerPolicyUpdate(TofinoSequencerPolicy, PolicyChangeReason), TofinoSequencerTick(TofinoSequencerPolicy, TofinoStateDetails), TofinoSequencerAbort { state: TofinoSeqState, @@ -408,8 +409,22 @@ impl idl::InOrderSequencerImpl for ServerImpl { _msg: &userlib::RecvMessage, policy: TofinoSequencerPolicy, ) -> Result<(), RequestError> { - ringbuf_entry!(Trace::TofinoSequencerPolicyUpdate(policy)); - self.tofino.policy = policy; + ringbuf_entry!(Trace::TofinoSequencerPolicyUpdate( + policy, + PolicyChangeReason::Other + )); + self.tofino.set_policy(policy, PolicyChangeReason::Other); + Ok(()) + } + + fn set_tofino_seq_policy_with_reason( + &mut self, + _msg: &userlib::RecvMessage, + policy: TofinoSequencerPolicy, + reason: PolicyChangeReason, + ) -> Result<(), RequestError> { + ringbuf_entry!(Trace::TofinoSequencerPolicyUpdate(policy, reason)); + self.tofino.set_policy(policy, reason); Ok(()) } @@ -420,6 +435,16 @@ impl idl::InOrderSequencerImpl for ServerImpl { Ok(self.tofino.sequencer.state().map_err(SeqError::from)?) } + fn tofino_seq_state_with_reason( + &mut self, + _: &RecvMessage, + ) -> Result> { + Ok(TofinoSeqStateWithReason { + state: self.tofino.sequencer.state().map_err(SeqError::from)?, + reason: self.tofino.reason, + }) + } + fn tofino_seq_error( &mut self, _: &RecvMessage, @@ -1065,7 +1090,10 @@ fn main() -> ! { { ringbuf_entry!(Trace::SkipLoadingClockConfiguration); server.clock_generator.config_loaded = true; - server.tofino.policy = TofinoSequencerPolicy::LatchOffOnFault; + server.tofino.set_policy( + TofinoSequencerPolicy::LatchOffOnFault, + PolicyChangeReason::InitialPowerOn, + ); } else if server.clock_generator.load_config().is_err() { panic!() } @@ -1127,7 +1155,11 @@ fn main() -> ! { // Power on, unless suppressed by the `stay-in-a2` feature if !cfg!(feature = "stay-in-a2") { - server.tofino.policy = TofinoSequencerPolicy::LatchOffOnFault; + // TODO what's the right reason? + server.tofino.set_policy( + TofinoSequencerPolicy::LatchOffOnFault, + PolicyChangeReason::InitialPowerOn, + ); } // @@ -1152,8 +1184,9 @@ ereports::declare_ereporter! { mod idl { use super::{ DebugPortState, DirectBarSegment, FanModuleIndex, FanModulePresence, - FanModuleStatus, FpgaError, SeqError, TofinoPcieReset, TofinoSeqError, - TofinoSeqState, TofinoSeqStep, TofinoSequencerPolicy, + FanModuleStatus, FpgaError, PolicyChangeReason, SeqError, + TofinoPcieReset, TofinoSeqError, TofinoSeqState, + TofinoSeqStateWithReason, TofinoSeqStep, TofinoSequencerPolicy, }; include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); diff --git a/drv/sidecar-seq-server/src/tofino.rs b/drv/sidecar-seq-server/src/tofino.rs index cb1d906d90..966eda916c 100644 --- a/drv/sidecar-seq-server/src/tofino.rs +++ b/drv/sidecar-seq-server/src/tofino.rs @@ -7,6 +7,7 @@ use drv_i2c_devices::raa229618::Raa229618; pub(crate) struct Tofino { pub policy: TofinoSequencerPolicy, + pub reason: PolicyChangeReason, pub sequencer: Sequencer, pub debug_port: DebugPort, pub vddcore: Raa229618, @@ -22,6 +23,7 @@ impl Tofino { let vddcore = Raa229618::new(&i2c_device, rail); Self { policy: TofinoSequencerPolicy::Disabled, + reason: PolicyChangeReason::InitialPowerOn, sequencer: Sequencer::new(MAINBOARD.get_task_id()), debug_port: DebugPort::new(MAINBOARD.get_task_id()), vddcore, @@ -32,6 +34,15 @@ impl Tofino { } } + pub fn set_policy( + &mut self, + policy: TofinoSequencerPolicy, + reason: PolicyChangeReason, + ) { + self.policy = policy; + self.reason = reason; + } + pub fn apply_vid(&mut self, vid: Tofino2Vid) -> Result<(), SeqError> { use userlib::units::Volts; diff --git a/idl/cpu-seq.idol b/idl/cpu-seq.idol index 206f97a03f..91079534c9 100644 --- a/idl/cpu-seq.idol +++ b/idl/cpu-seq.idol @@ -12,6 +12,13 @@ Interface( )), idempotent: true, ), + "get_state_with_reason": ( + doc: "Return the power state and the reason for its last change", + args: {}, + reply: Simple("PowerStateWithReason"), + idempotent: true, + encoding: Hubpack, + ), "set_state": ( doc: "Set the power state without providing a reason (legacy).", args: { diff --git a/idl/sidecar-seq.idol b/idl/sidecar-seq.idol index 5fedf98c9e..c90be8749c 100644 --- a/idl/sidecar-seq.idol +++ b/idl/sidecar-seq.idol @@ -15,11 +15,28 @@ Interface( ), ), "set_tofino_seq_policy": ( + doc: "Set the power state, without providing a reason (legacy)", + args: { + "policy": ( + type: "TofinoSequencerPolicy", + recv: FromPrimitive("u8"), + ), + }, + reply: Result( + ok: "()", + err: CLike("SeqError"), + ), + ), + "set_tofino_seq_policy_with_reason": ( doc: "Set the power state", args: { "policy": ( type: "TofinoSequencerPolicy", recv: FromPrimitive("u8"), + ), + "reason": ( + type: "PolicyChangeReason", + recv: FromPrimitive("u8"), ) }, reply: Result( @@ -38,6 +55,15 @@ Interface( err: CLike("SeqError"), ), ), + "tofino_seq_state_with_reason": ( + doc: "Return the Tofino sequencer state and the reason for last policy change", + args: {}, + reply: Result( + ok: "TofinoSeqStateWithReason", + err: CLike("SeqError"), + ), + encoding: Hubpack, + ), "tofino_seq_error": ( doc: "Return the Tofino sequencer error, if any", args: {}, diff --git a/task/control-plane-agent/src/main.rs b/task/control-plane-agent/src/main.rs index bfb414e2b3..1afecd9a03 100644 --- a/task/control-plane-agent/src/main.rs +++ b/task/control-plane-agent/src/main.rs @@ -136,6 +136,7 @@ enum MgsMessage { component: SpComponent, }, GetPowerState, + GetPowerStateWithReason, SetPowerState(PowerState), Inventory, HostPhase2Data { diff --git a/task/control-plane-agent/src/mgs_compute_sled.rs b/task/control-plane-agent/src/mgs_compute_sled.rs index 503402e017..dc4ccb99f7 100644 --- a/task/control-plane-agent/src/mgs_compute_sled.rs +++ b/task/control-plane-agent/src/mgs_compute_sled.rs @@ -19,10 +19,11 @@ use gateway_messages::{ ComponentDetails, ComponentUpdatePrepare, DiscoverResponse, DumpSegment, DumpTask, GpioToggleCount, Header, IgnitionCommand, IgnitionState, LastPostCode, Message, MessageKind, MgsError, MgsRequest, MgsResponse, - PostCode, PowerState, PowerStateTransition, RotBootInfo, RotRequest, - RotResponse, SERIAL_CONSOLE_IDLE_TIMEOUT, SensorRequest, SensorResponse, - SpComponent, SpError, SpPort as GwSpPort, SpRequest, SpStateV2, - SpUpdatePrepare, UpdateChunk, UpdateId, UpdateStatus, ignition, + PostCode, PowerState, PowerStateTransition, PowerStateWithReason, + RotBootInfo, RotRequest, RotResponse, SERIAL_CONSOLE_IDLE_TIMEOUT, + SensorRequest, SensorResponse, SpComponent, SpError, SpPort as GwSpPort, + SpRequest, SpStateV2, SpUpdatePrepare, StateChangeReason, UpdateChunk, + UpdateId, UpdateStatus, ignition, }; use heapless::{Deque, Vec}; use host_sp_messages::HostStartupOptions; @@ -437,15 +438,17 @@ impl MgsHandler { Ok(()) } - fn power_state_impl(&self) -> Result { + fn power_state_impl(&self) -> Result { use drv_cpu_seq_api::PowerState as DrvPowerState; + use drv_cpu_seq_api::StateChangeReason as DrvStateChangeReason; // TODO Do we want to expose the sub-states to the control plane? For // now, squish them down. // // TODO Do we want to expose A1 to the control plane at all? If not, // what would we map it to? Maybe easier to leave it exposed. - let state = match self.sequencer.get_state() { + let drv_state = self.sequencer.get_state_with_reason(); + let state = match drv_state.state { DrvPowerState::A2 | DrvPowerState::A2PlusFans => PowerState::A2, DrvPowerState::A0 | DrvPowerState::A0PlusHP @@ -453,7 +456,31 @@ impl MgsHandler { | DrvPowerState::A0Reset => PowerState::A0, }; - Ok(state) + let reason = match drv_state.reason { + DrvStateChangeReason::Other => StateChangeReason::Other, + DrvStateChangeReason::InitialPowerOn => { + StateChangeReason::InitialPowerOn + } + DrvStateChangeReason::ControlPlane => { + StateChangeReason::ControlPlane + } + DrvStateChangeReason::CpuReset => StateChangeReason::CpuReset, + DrvStateChangeReason::HostBootFailure => { + StateChangeReason::HostBootFailure + } + DrvStateChangeReason::HostPanic => StateChangeReason::HostPanic, + DrvStateChangeReason::HostPowerOff => { + StateChangeReason::HostPowerOff + } + DrvStateChangeReason::HostReboot => StateChangeReason::HostReboot, + DrvStateChangeReason::Overheat => StateChangeReason::Overheat, + DrvStateChangeReason::A0Mapo => StateChangeReason::A0Mapo, + DrvStateChangeReason::SmerrAssert => StateChangeReason::SmerrAssert, + DrvStateChangeReason::NicMapo => StateChangeReason::NicMapo, + DrvStateChangeReason::Unknown => StateChangeReason::Unknown, + }; + + Ok(PowerStateWithReason { state, reason }) } } @@ -554,7 +581,7 @@ impl SpHandler for MgsHandler { } fn sp_state(&mut self) -> Result { - let power_state = self.power_state_impl()?; + let power_state = self.power_state_impl()?.state; self.common.sp_state(power_state) } @@ -711,6 +738,16 @@ impl SpHandler for MgsHandler { fn power_state(&mut self) -> Result { ringbuf_entry_root!(Log::MgsMessage(MgsMessage::GetPowerState)); + self.power_state_impl() + .map(|state_with_reason| state_with_reason.state) + } + + fn power_state_with_reason( + &mut self, + ) -> Result { + ringbuf_entry_root!(Log::MgsMessage( + MgsMessage::GetPowerStateWithReason + )); self.power_state_impl() } diff --git a/task/control-plane-agent/src/mgs_minibar.rs b/task/control-plane-agent/src/mgs_minibar.rs index 0421d92508..7303986674 100644 --- a/task/control-plane-agent/src/mgs_minibar.rs +++ b/task/control-plane-agent/src/mgs_minibar.rs @@ -18,9 +18,10 @@ use gateway_messages::{ ComponentAction, ComponentActionResponse, ComponentDetails, ComponentUpdatePrepare, DiscoverResponse, DumpSegment, DumpTask, IgnitionCommand, IgnitionState, MgsError, MgsRequest, MgsResponse, - PowerState, PowerStateTransition, RotBootInfo, RotRequest, RotResponse, - SensorRequest, SensorResponse, SpComponent, SpError, SpStateV2, - SpUpdatePrepare, UpdateChunk, UpdateId, UpdateStatus, ignition, + PowerState, PowerStateTransition, PowerStateWithReason, RotBootInfo, + RotRequest, RotResponse, SensorRequest, SensorResponse, SpComponent, + SpError, SpStateV2, SpUpdatePrepare, StateChangeReason, UpdateChunk, + UpdateId, UpdateStatus, ignition, }; use host_sp_messages::HostStartupOptions; use idol_runtime::{Leased, RequestError}; @@ -129,9 +130,12 @@ impl MgsHandler { Err(ControlPlaneAgentError::OperationUnsupported.into()) } - fn power_state_impl(&self) -> Result { + fn power_state_impl(&self) -> Result { // Minibar has no configurable power states. - Ok(PowerState::A2) + Ok(PowerStateWithReason { + state: PowerState::A2, + reason: StateChangeReason::InitialPowerOn, + }) } } @@ -231,7 +235,7 @@ impl SpHandler for MgsHandler { } fn sp_state(&mut self) -> Result { - let power_state = self.power_state_impl()?; + let power_state = self.power_state_impl()?.state; self.common.sp_state(power_state) } @@ -338,6 +342,16 @@ impl SpHandler for MgsHandler { fn power_state(&mut self) -> Result { ringbuf_entry_root!(Log::MgsMessage(MgsMessage::GetPowerState)); + self.power_state_impl() + .map(|state_with_reason| state_with_reason.state) + } + + fn power_state_with_reason( + &mut self, + ) -> Result { + ringbuf_entry_root!(Log::MgsMessage( + MgsMessage::GetPowerStateWithReason + )); self.power_state_impl() } diff --git a/task/control-plane-agent/src/mgs_psc.rs b/task/control-plane-agent/src/mgs_psc.rs index a88b4baad3..b1346d45f7 100644 --- a/task/control-plane-agent/src/mgs_psc.rs +++ b/task/control-plane-agent/src/mgs_psc.rs @@ -15,9 +15,10 @@ use gateway_messages::{ ComponentAction, ComponentActionResponse, ComponentDetails, ComponentUpdatePrepare, DiscoverResponse, DumpSegment, DumpTask, IgnitionCommand, IgnitionState, MgsError, MgsRequest, MgsResponse, - PowerState, PowerStateTransition, RotBootInfo, RotRequest, RotResponse, - SensorRequest, SensorResponse, SpComponent, SpError, SpStateV2, - SpUpdatePrepare, UpdateChunk, UpdateId, UpdateStatus, ignition, + PowerState, PowerStateTransition, PowerStateWithReason, RotBootInfo, + RotRequest, RotResponse, SensorRequest, SensorResponse, SpComponent, + SpError, SpStateV2, SpUpdatePrepare, StateChangeReason, UpdateChunk, + UpdateId, UpdateStatus, ignition, }; use host_sp_messages::HostStartupOptions; use idol_runtime::{Leased, RequestError}; @@ -130,9 +131,12 @@ impl MgsHandler { Err(ControlPlaneAgentError::InvalidStartupOptions.into()) } - fn power_state_impl(&self) -> Result { + fn power_state_impl(&self) -> Result { // We have no states other than A2. - Ok(PowerState::A2) + Ok(PowerStateWithReason { + state: PowerState::A2, + reason: StateChangeReason::InitialPowerOn, + }) } } @@ -233,7 +237,7 @@ impl SpHandler for MgsHandler { } fn sp_state(&mut self) -> Result { - let power_state = self.power_state_impl()?; + let power_state = self.power_state_impl()?.state; self.common.sp_state(power_state) } @@ -353,6 +357,16 @@ impl SpHandler for MgsHandler { fn power_state(&mut self) -> Result { ringbuf_entry_root!(Log::MgsMessage(MgsMessage::GetPowerState)); + self.power_state_impl() + .map(|state_with_reason| state_with_reason.state) + } + + fn power_state_with_reason( + &mut self, + ) -> Result { + ringbuf_entry_root!(Log::MgsMessage( + MgsMessage::GetPowerStateWithReason + )); self.power_state_impl() } diff --git a/task/control-plane-agent/src/mgs_sidecar.rs b/task/control-plane-agent/src/mgs_sidecar.rs index cd5c482b97..7243e16955 100644 --- a/task/control-plane-agent/src/mgs_sidecar.rs +++ b/task/control-plane-agent/src/mgs_sidecar.rs @@ -12,7 +12,7 @@ use crate::{ usize_max, }; use drv_monorail_api::{Monorail, MonorailError}; -use drv_sidecar_seq_api::Sequencer; +use drv_sidecar_seq_api::{PolicyChangeReason, Sequencer}; use drv_transceivers_api::Transceivers; use enum_map::EnumMap; use gateway_messages::sp_impl::{ @@ -24,10 +24,11 @@ use gateway_messages::{ EcdsaSha2Nistp256Challenge, IgnitionCommand, IgnitionState, MgsError, MgsRequest, MgsResponse, MonorailComponentAction, MonorailComponentActionResponse, MonorailError as GwMonorailError, - PcieRegisterRead, PowerState, PowerStateTransition, RotBootInfo, - RotRequest, RotResponse, SensorRequest, SensorResponse, SpComponent, - SpError, SpStateV2, SpUpdatePrepare, UnlockChallenge, UnlockResponse, - UpdateChunk, UpdateId, UpdateStatus, ignition, + PcieRegisterRead, PowerState, PowerStateTransition, PowerStateWithReason, + RotBootInfo, RotRequest, RotResponse, SensorRequest, SensorResponse, + SpComponent, SpError, SpStateV2, SpUpdatePrepare, StateChangeReason, + UnlockChallenge, UnlockResponse, UpdateChunk, UpdateId, UpdateStatus, + ignition, }; use host_sp_messages::HostStartupOptions; use idol_runtime::{Leased, RequestError}; @@ -250,23 +251,31 @@ impl MgsHandler { Err(ControlPlaneAgentError::InvalidStartupOptions.into()) } - fn power_state_impl(&self) -> Result { + fn power_state_impl(&self) -> Result { use drv_sidecar_seq_api::TofinoSeqState; // TODO Is this mapping of the sub-states correct? Do we want to expose // them to the control plane somehow (probably not)? - let state = match self - .sequencer - .tofino_seq_state() - .map_err(|e| SpError::PowerStateError(e as u32))? - { + let state_with_reason = + self.sequencer + .tofino_seq_state_with_reason() + .map_err(|e| SpError::PowerStateError(e as u32))?; + let state = match state_with_reason.state { TofinoSeqState::Init | TofinoSeqState::InPowerDown | TofinoSeqState::A2 => PowerState::A2, TofinoSeqState::InPowerUp | TofinoSeqState::A0 => PowerState::A0, }; + let reason = match state_with_reason.reason { + PolicyChangeReason::Other => StateChangeReason::Other, + PolicyChangeReason::InitialPowerOn => { + StateChangeReason::InitialPowerOn + } + PolicyChangeReason::ControlPlane => StateChangeReason::ControlPlane, + PolicyChangeReason::Overheat => StateChangeReason::Overheat, + }; - Ok(state) + Ok(PowerStateWithReason { state, reason }) } /// Unlocks the tech port if the challenge and response are compatible @@ -592,7 +601,7 @@ impl SpHandler for MgsHandler { } fn sp_state(&mut self) -> Result { - let power_state = self.power_state_impl()?; + let power_state = self.power_state_impl()?.state; self.common.sp_state(power_state) } @@ -780,6 +789,16 @@ impl SpHandler for MgsHandler { fn power_state(&mut self) -> Result { ringbuf_entry_root!(Log::MgsMessage(MgsMessage::GetPowerState)); + self.power_state_impl() + .map(|state_with_reason| state_with_reason.state) + } + + fn power_state_with_reason( + &mut self, + ) -> Result { + ringbuf_entry_root!(Log::MgsMessage( + MgsMessage::GetPowerStateWithReason + )); self.power_state_impl() } @@ -816,9 +835,11 @@ impl SpHandler for MgsHandler { .clear_tofino_seq_error() .map_err(|e| SpError::PowerStateError(e as u32))? } - self.sequencer - .set_tofino_seq_policy(policy) + .set_tofino_seq_policy_with_reason( + policy, + PolicyChangeReason::ControlPlane, + ) .map_err(|e| SpError::PowerStateError(e as u32))?; // TODO(eliza): this should probably also be made idempotent, à la the diff --git a/task/thermal/src/bsp/sidecar_bcd.rs b/task/thermal/src/bsp/sidecar_bcd.rs index bb062eac2b..ce8e4b578d 100644 --- a/task/thermal/src/bsp/sidecar_bcd.rs +++ b/task/thermal/src/bsp/sidecar_bcd.rs @@ -10,7 +10,9 @@ use crate::control::{ }; use drv_i2c_devices::tmp451::*; pub use drv_sidecar_seq_api::SeqError; -use drv_sidecar_seq_api::{Sequencer, TofinoSeqState, TofinoSequencerPolicy}; +use drv_sidecar_seq_api::{ + PolicyChangeReason, Sequencer, TofinoSeqState, TofinoSequencerPolicy, +}; use task_sensor_api::SensorId; use task_thermal_api::ThermalProperties; use userlib::{TaskId, UnwrapLite, task_slot, units::Celsius}; @@ -151,8 +153,10 @@ impl Bsp { } pub fn power_down(&self) -> Result<(), SeqError> { - self.seq - .set_tofino_seq_policy(TofinoSequencerPolicy::Disabled) + self.seq.set_tofino_seq_policy_with_reason( + TofinoSequencerPolicy::Disabled, + PolicyChangeReason::Overheat, + ) } pub fn get_fan_presence(&self) -> Result, SeqError> {