From ec9c966b64460b990035d27a59347d1002496423 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Mon, 26 Feb 2024 15:04:59 -0500 Subject: [PATCH] Add command to read the VPD lock state The lock information is returned as an array of trailing data. --- faux-mgs/src/main.rs | 16 ++++++ gateway-messages/src/lib.rs | 2 +- gateway-messages/src/mgs_to_sp.rs | 4 ++ gateway-messages/src/sp_impl.rs | 19 +++++++ gateway-messages/src/sp_to_mgs.rs | 56 +++++++++++++++++++ gateway-messages/tests/versioning/mod.rs | 1 + gateway-messages/tests/versioning/v11.rs | 65 ++++++++++++++++++++++ gateway-sp-comms/src/single_sp.rs | 6 ++ gateway-sp-comms/src/sp_response_expect.rs | 8 +++ 9 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 gateway-messages/tests/versioning/v11.rs diff --git a/faux-mgs/src/main.rs b/faux-mgs/src/main.rs index 2ab85ce0..dfb5c4f4 100644 --- a/faux-mgs/src/main.rs +++ b/faux-mgs/src/main.rs @@ -363,6 +363,9 @@ enum Command { #[clap(short, long, default_value_t = CfpaSlot::Active)] slot: CfpaSlot, }, + + /// Reads the lock status of any VPD in the system + VpdLockStatus, } #[derive(Subcommand, Debug, Clone)] @@ -1284,6 +1287,19 @@ async fn run_command( }?; handle_cxpa("cfpa", data, out, json) } + Command::VpdLockStatus => { + let data = sp.vpd_lock_status_all().await?; + + if json { + Ok(Output::Json(json!({ "vpd_lock_status": data }))) + } else { + let mut out = vec![]; + for b in data { + out.push(format!("{b:x?}")); + } + Ok(Output::Lines(out)) + } + } } } diff --git a/gateway-messages/src/lib.rs b/gateway-messages/src/lib.rs index b1e86a09..2eadf2ef 100644 --- a/gateway-messages/src/lib.rs +++ b/gateway-messages/src/lib.rs @@ -66,7 +66,7 @@ pub const ROT_PAGE_SIZE: usize = 512; /// for more detail and discussion. pub mod version { pub const MIN: u32 = 2; - pub const CURRENT: u32 = 10; + pub const CURRENT: u32 = 11; } #[derive( diff --git a/gateway-messages/src/mgs_to_sp.rs b/gateway-messages/src/mgs_to_sp.rs index 6723ca6d..333d5bf1 100644 --- a/gateway-messages/src/mgs_to_sp.rs +++ b/gateway-messages/src/mgs_to_sp.rs @@ -169,6 +169,10 @@ pub enum MgsRequest { /// Issues a sensor read request ReadRot(RotRequest), + + /// Dump information about the lock state of the VPD (Vital Product Data) + /// The values are serialized in the trailer of the packet + VpdLockState, } #[derive( diff --git a/gateway-messages/src/sp_impl.rs b/gateway-messages/src/sp_impl.rs index 57b72ee3..62e2f285 100644 --- a/gateway-messages/src/sp_impl.rs +++ b/gateway-messages/src/sp_impl.rs @@ -391,6 +391,9 @@ pub trait SpHandler { request: RotRequest, buf: &mut [u8], ) -> Result; + + fn vpd_lock_status_all(&mut self, buf: &mut [u8]) + -> Result; } /// Handle a single incoming message. @@ -949,6 +952,15 @@ fn handle_mgs_request( MgsRequest::CurrentTime => { handler.current_time().map(SpResponse::CurrentTime) } + MgsRequest::VpdLockState => { + let r = handler.vpd_lock_status_all(trailing_tx_buf); + + if let Ok(n) = r { + outgoing_trailing_data = + Some(OutgoingTrailingData::ShiftFromTail(n)); + } + r.map(|_| SpResponse::VpdLockState) + } }; let response = match result { @@ -1346,6 +1358,13 @@ mod tests { fn current_time(&mut self) -> Result { unimplemented!() } + + fn vpd_lock_status_all( + &mut self, + _buf: &mut [u8], + ) -> Result { + unimplemented!() + } } #[cfg(feature = "std")] diff --git a/gateway-messages/src/sp_to_mgs.rs b/gateway-messages/src/sp_to_mgs.rs index 048c73f8..03617f72 100644 --- a/gateway-messages/src/sp_to_mgs.rs +++ b/gateway-messages/src/sp_to_mgs.rs @@ -126,6 +126,8 @@ pub enum SpResponse { ReadSensor(SensorResponse), CurrentTime(u64), ReadRot(RotResponse), + /// The packet contains trailing lock information + VpdLockState, } /// Identifier for one of of an SP's KSZ8463 management-network-facing ports. @@ -554,6 +556,7 @@ pub enum SpError { Sprockets(SprocketsError), Update(UpdateError), Sensor(SensorError), + Vpd(VpdError), } impl fmt::Display for SpError { @@ -664,6 +667,7 @@ impl fmt::Display for SpError { Self::Sprockets(e) => write!(f, "sprockets: {}", e), Self::Update(e) => write!(f, "update: {}", e), Self::Sensor(e) => write!(f, "sensor: {}", e), + Self::Vpd(e) => write!(f, "vpd: {}", e), } } } @@ -964,3 +968,55 @@ impl fmt::Display for SensorError { #[cfg(feature = "std")] impl std::error::Error for SensorError {} + +/// VPD errors encountered while reading +/// +/// This value is wrapped by [`SpError`] +#[derive( + Debug, Clone, Copy, PartialEq, SerializedSize, Serialize, Deserialize, +)] +pub enum VpdError { + InvalidDevice, + NotPresent, + DeviceError, + Unavailable, + DeviceTimeout, + DeviceOff, + BadAddress, + BadBuffer, + BadRead, + BadWrite, + BadLock, + NotImplemented, + IsLocked, + PartiallyLocked, + AlreadyLocked, + TaskRestarted, +} + +impl fmt::Display for VpdError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidDevice => write!(f, "device index is invalid"), + Self::NotPresent => write!(f, "device is not present"), + Self::DeviceError => write!(f, "error with VPD device"), + Self::Unavailable => write!(f, "vpd device is unavailable"), + Self::DeviceTimeout => write!(f, "vpd device timed out"), + Self::DeviceOff => write!(f, "vpd device is off"), + Self::BadAddress => write!(f, "bad address"), + Self::BadBuffer => write!(f, "bad buffer"), + Self::BadRead => write!(f, "bad read"), + Self::BadWrite => write!(f, "bad write"), + Self::BadLock => write!(f, "lock failed"), + Self::NotImplemented => { + write!(f, "Feature is not implemented/compiled out") + } + Self::IsLocked => write!(f, "VPD is locked, cannot write"), + Self::PartiallyLocked => write!(f, "VPD is partially locked"), + Self::AlreadyLocked => { + write!(f, "VPD is already locked, cannot lock again") + } + Self::TaskRestarted => write!(f, "task restarted"), + } + } +} diff --git a/gateway-messages/tests/versioning/mod.rs b/gateway-messages/tests/versioning/mod.rs index 60f2b29b..272a5de3 100644 --- a/gateway-messages/tests/versioning/mod.rs +++ b/gateway-messages/tests/versioning/mod.rs @@ -15,6 +15,7 @@ mod v07; mod v08; mod v09; mod v10; +mod v11; pub fn assert_serialized( out: &mut [u8], diff --git a/gateway-messages/tests/versioning/v11.rs b/gateway-messages/tests/versioning/v11.rs new file mode 100644 index 00000000..3b8893b2 --- /dev/null +++ b/gateway-messages/tests/versioning/v11.rs @@ -0,0 +1,65 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! The tests in this module check that the serialized form of messages from MGS +//! protocol version 11 have not changed. +//! +//! If a test in this module fails, _do not change the test_! This means you +//! have changed, deleted, or reordered an existing message type or enum +//! variant, and you should revert that change. This will remain true until we +//! bump the `version::MIN` to a value higher than 11, at which point these +//! tests can be removed as we will stop supporting v11. + +use super::assert_serialized; +use gateway_messages::MgsRequest; +use gateway_messages::SerializedSize; +use gateway_messages::SpError; +use gateway_messages::SpResponse; +use gateway_messages::VpdError; + +#[test] +fn sp_response() { + let mut out = [0; SpResponse::MAX_SIZE]; + let response = SpResponse::VpdLockState; + let expected = [41]; + assert_serialized(&mut out, &expected, &response); +} + +#[test] +fn host_request() { + let mut out = [0; MgsRequest::MAX_SIZE]; + let request = MgsRequest::VpdLockState; + let expected = [41]; + assert_serialized(&mut out, &expected, &request); +} + +#[test] +fn vpd_protocol_errors() { + let mut out = [0; MgsRequest::MAX_SIZE]; + + for (error, serialized) in [ + (VpdError::InvalidDevice, &[0]), + (VpdError::NotPresent, &[1]), + (VpdError::DeviceError, &[2]), + (VpdError::Unavailable, &[3]), + (VpdError::DeviceTimeout, &[4]), + (VpdError::DeviceOff, &[5]), + (VpdError::BadAddress, &[6]), + (VpdError::BadBuffer, &[7]), + (VpdError::BadRead, &[8]), + (VpdError::BadWrite, &[9]), + (VpdError::BadLock, &[10]), + (VpdError::NotImplemented, &[11]), + (VpdError::IsLocked, &[12]), + (VpdError::PartiallyLocked, &[13]), + (VpdError::AlreadyLocked, &[14]), + (VpdError::TaskRestarted, &[15]), + ] { + // Test VpdError variants encoded in an SpResponse + let response = SpResponse::Error(SpError::Vpd(error)); + let mut expected = vec![17, 34]; + expected.extend_from_slice(serialized); + assert_serialized(&mut out, &expected, &response); + } +} diff --git a/gateway-sp-comms/src/single_sp.rs b/gateway-sp-comms/src/single_sp.rs index 29fa7314..040ca7c9 100644 --- a/gateway-sp-comms/src/single_sp.rs +++ b/gateway-sp-comms/src/single_sp.rs @@ -841,6 +841,12 @@ impl SingleSp { .await .and_then(expect_read_rot) } + + pub async fn vpd_lock_status_all(&self) -> Result> { + let result = rpc(&self.cmds_tx, MgsRequest::VpdLockState, None).await; + + result.result.and_then(expect_vpd_lock_state) + } } // Helper trait to call a "paginated" (i.e., split across multiple UDP packets) diff --git a/gateway-sp-comms/src/sp_response_expect.rs b/gateway-sp-comms/src/sp_response_expect.rs index ecc628a3..f10984f6 100644 --- a/gateway-sp-comms/src/sp_response_expect.rs +++ b/gateway-sp-comms/src/sp_response_expect.rs @@ -131,6 +131,14 @@ pub(crate) fn expect_caboose_value( expect_caboose_value(r).map(|((), data)| data) } +pub(crate) fn expect_vpd_lock_state( + r: (SocketAddrV6, SpResponse, Vec), +) -> Result> { + // Wrapper around the autogenerated function for a nicer return type + expect_data_fn!(VpdLockState); + expect_vpd_lock_state(r).map(|((), data)| data) +} + /// Expects a `RotResponse::Ok` followed by 512 bytes of trailing data /// /// Returns just the trailing data on success, and an error in other cases