Skip to content
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ authors = ["Caliptra contributors", "OpenPRoT contributors"]
edition = "2024"

[dependencies]
zerocopy = {version = "0.8.17", features = ["derive"]}
zerocopy = { version = "0.8.17", features = ["derive"] }
bitfield = "0.14.0"
33 changes: 33 additions & 0 deletions src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use zerocopy::{FromBytes, Immutable, IntoBytes};
pub enum PldmCodecError {
BufferTooShort,
Unsupported,
InvalidData,
}

/// A trait for encoding and decoding PLDM (Platform Level Data Model) messages.
Expand Down Expand Up @@ -37,7 +38,39 @@ pub trait PldmCodec: core::fmt::Debug + Sized {
fn decode(buffer: &[u8]) -> Result<Self, PldmCodecError>;
}

/// A trait for encoding and decoding PLDM messages with explicit lifetime requirements.
///
/// This trait is similar to `PldmCodec` but supports types that borrow data from the buffer
/// during decoding, requiring an explicit lifetime parameter.
pub trait PldmCodecWithLifetime<'a>: core::fmt::Debug + Sized {
/// Encodes the PLDM message into the provided byte buffer.
///
/// # Arguments
///
/// * `buffer` - A mutable reference to a byte slice where the encoded message will be stored.
///
/// # Returns
///
/// A `Result` containing the size of the encoded message on success, or a `PldmCodecError` on failure.
fn encode(&self, buffer: &mut [u8]) -> Result<usize, PldmCodecError>;

/// Decodes a PLDM message from the provided byte buffer.
///
/// # Arguments
///
/// * `buffer` - A reference to a byte slice containing the encoded message. The decoded
/// type may hold references to this buffer.
///
/// # Returns
///
/// A `Result` containing the decoded message on success, or a `PldmCodecError` on failure.
fn decode(buffer: &'a [u8]) -> Result<Self, PldmCodecError>;
}

// Default implementation of PldmCodec for types that can leverage zerocopy.
// TODO: can we generalize this to use sub-struct encodes when possible?
// There are structs like PldmFirmwareString that contain variable-length data
// that would need special handling.
impl<T> PldmCodec for T
where
T: core::fmt::Debug + Sized + FromBytes + IntoBytes + Immutable,
Expand Down
48 changes: 43 additions & 5 deletions src/message/firmware_update/get_fw_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,7 @@ impl PldmCodec for GetFirmwareParametersResponse {
offset += core::mem::size_of::<u8>();

let bytes = self.parms.encode(&mut buffer[offset..])?;
offset += bytes;
Ok(offset)
Ok(offset + bytes)
}

fn decode(buffer: &[u8]) -> Result<Self, PldmCodecError> {
Expand Down Expand Up @@ -331,32 +330,71 @@ mod test {
}

#[test]
fn test_get_firmware_parameters_request() {
fn test_get_firmware_parameters_request_codec() {
let request = GetFirmwareParametersRequest::new(0, PldmMsgType::Request);
let mut buffer = [0u8; PLDM_MSG_HEADER_LEN];
request.encode(&mut buffer).unwrap();

let decoded_request = GetFirmwareParametersRequest::decode(&buffer).unwrap();
assert_eq!(request, decoded_request);

// Test buffer too short error
let mut buffer = [0u8; PLDM_MSG_HEADER_LEN - 1];
assert_eq!(
request.encode(&mut buffer).unwrap_err(),
PldmCodecError::BufferTooShort
);

assert_eq!(
GetFirmwareParametersRequest::decode(&buffer).unwrap_err(),
PldmCodecError::BufferTooShort
);
}

#[test]
fn test_get_firmware_parameters() {
fn test_get_firmware_parameters_codec() {
let firmware_parameters = construct_firmware_params();
let mut buffer = [0u8; 1024];
let size = firmware_parameters.encode(&mut buffer).unwrap();
assert_eq!(size, firmware_parameters.codec_size_in_bytes());

let decoded_firmware_parameters = FirmwareParameters::decode(&buffer[..size]).unwrap();
assert_eq!(firmware_parameters, decoded_firmware_parameters);

// Test buffer too short error
let mut short_buffer = [0u8; 1];
assert_eq!(
firmware_parameters.encode(&mut short_buffer).unwrap_err(),
PldmCodecError::BufferTooShort
);

assert_eq!(
FirmwareParameters::decode(&short_buffer).unwrap_err(),
PldmCodecError::BufferTooShort
);
}

#[test]
fn test_get_firmware_parameters_response() {
fn test_get_firmware_parameters_response_codec() {
let firmware_parameters = construct_firmware_params();
let response = GetFirmwareParametersResponse::new(0, 0, &firmware_parameters);
let mut buffer = [0u8; 1024];
let size = response.encode(&mut buffer).unwrap();
assert_eq!(size, response.codec_size_in_bytes());

let decoded_response = GetFirmwareParametersResponse::decode(&buffer[..size]).unwrap();
assert_eq!(response, decoded_response);

// Test buffer too short error
let mut short_buffer = [0u8; 1];
assert_eq!(
response.encode(&mut short_buffer).unwrap_err(),
PldmCodecError::BufferTooShort
);

assert_eq!(
GetFirmwareParametersResponse::decode(&short_buffer).unwrap_err(),
PldmCodecError::BufferTooShort
);
}
}
Loading