Skip to content

Commit ecd1727

Browse files
Your Nameleongross
authored andcommitted
fd-commands: undo dynamic buffer sizes and add tests
Signed-off-by: leongross <[email protected]>
1 parent 44ac097 commit ecd1727

File tree

3 files changed

+272
-19
lines changed

3 files changed

+272
-19
lines changed

src/codec.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,35 @@ pub trait PldmCodec: core::fmt::Debug + Sized {
3737
fn decode(buffer: &[u8]) -> Result<Self, PldmCodecError>;
3838
}
3939

40+
/// A trait for encoding and decoding PLDM messages with explicit lifetime requirements.
41+
///
42+
/// This trait is similar to `PldmCodec` but supports types that borrow data from the buffer
43+
/// during decoding, requiring an explicit lifetime parameter.
44+
pub trait PldmCodecWithLifetime<'a>: core::fmt::Debug + Sized {
45+
/// Encodes the PLDM message into the provided byte buffer.
46+
///
47+
/// # Arguments
48+
///
49+
/// * `buffer` - A mutable reference to a byte slice where the encoded message will be stored.
50+
///
51+
/// # Returns
52+
///
53+
/// A `Result` containing the size of the encoded message on success, or a `PldmCodecError` on failure.
54+
fn encode(&self, buffer: &mut [u8]) -> Result<usize, PldmCodecError>;
55+
56+
/// Decodes a PLDM message from the provided byte buffer.
57+
///
58+
/// # Arguments
59+
///
60+
/// * `buffer` - A reference to a byte slice containing the encoded message. The decoded
61+
/// type may hold references to this buffer.
62+
///
63+
/// # Returns
64+
///
65+
/// A `Result` containing the decoded message on success, or a `PldmCodecError` on failure.
66+
fn decode(buffer: &'a [u8]) -> Result<Self, PldmCodecError>;
67+
}
68+
4069
// Default implementation of PldmCodec for types that can leverage zerocopy.
4170
impl<T> PldmCodec for T
4271
where

src/message/firmware_update/get_package_data.rs

Lines changed: 223 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::protocol::base::{
77

88
use crate::pldm_completion_code;
99

10+
use crate::codec::{PldmCodec, PldmCodecError, PldmCodecWithLifetime};
1011
use crate::protocol::firmware_update::{FwUpdateCmd, FwUpdateCompletionCode};
1112
use zerocopy::{FromBytes, Immutable, IntoBytes};
1213

@@ -53,9 +54,11 @@ pldm_completion_code! {
5354
}
5455
}
5556

56-
#[derive(Debug, Clone, FromBytes, IntoBytes, Immutable, PartialEq)]
57-
#[repr(C, packed)]
58-
pub struct GetPackageDataResponse<'a> {
57+
const MAX_PORTION_DATA_SIZE: usize = 0xff;
58+
#[derive(Debug, Clone, PartialEq, FromBytes)]
59+
#[repr(C)]
60+
/// GetPackageDataResponse is parameterized over the portion package data size.
61+
pub struct GetPackageDataResponse {
5962
pub hdr: PldmMsgHeader<[u8; PLDM_MSG_HEADER_LEN]>,
6063

6164
/// PLDM_BASE_CODES, COMMAND_NOT_EXPECTED, NO_PACKAGE_DATA,
@@ -69,17 +72,26 @@ pub struct GetPackageDataResponse<'a> {
6972
/// If the FD provided a value in the GetPackageDataMaximumTransferSize field, then the UA should
7073
/// select the amount of data to return such that the byte length for this field, except when TransferFlag
7174
/// = End or StartAndEnd, is equal to or less than that value.
72-
pub portion_of_package_data: &'a [u8],
75+
pub portion_of_package_data: [u8; MAX_PORTION_DATA_SIZE],
76+
77+
// Non-spec field. Since the spec tells us that this can vary in unknown size,
78+
// we save the max and keep track of the actual size in this var.
79+
// When encoding this into byte form, we have to omit this field and only
80+
// encode as many bytes of [portion_of_package] as [portion_of_package_data_len]
81+
// tells us to.
82+
portion_of_package_data_len: usize,
7383
}
7484

75-
impl<'a> GetPackageDataResponse<'a> {
85+
impl GetPackageDataResponse {
7686
pub fn new(
7787
instance_id: InstanceId,
7888
completion_code: GetPackageDataCode,
7989
next_data_transfer_handle: u32,
8090
transfer_flag: TransferOperationFlag,
81-
portion_of_package_data: &'a [u8],
91+
portion_of_package_data: &[u8],
8292
) -> Self {
93+
let mut pdata: [u8; MAX_PORTION_DATA_SIZE] = [0x00; MAX_PORTION_DATA_SIZE];
94+
pdata[0..portion_of_package_data.len()].copy_from_slice(portion_of_package_data);
8395
GetPackageDataResponse {
8496
hdr: PldmMsgHeader::new(
8597
instance_id,
@@ -90,11 +102,101 @@ impl<'a> GetPackageDataResponse<'a> {
90102
completion_code: completion_code.into(),
91103
next_data_transfer_handle,
92104
transfer_flag: transfer_flag as u8,
93-
portion_of_package_data,
105+
portion_of_package_data: pdata,
106+
portion_of_package_data_len: portion_of_package_data.len(),
94107
}
95108
}
96109
}
97110

111+
// See: src/message/firmware_update/get_fw_params.rs for manual decode etc
112+
impl PldmCodec for GetPackageDataResponse {
113+
fn encode(&self, buffer: &mut [u8]) -> Result<usize, crate::codec::PldmCodecError> {
114+
if buffer.len()
115+
< core::mem::size_of::<GetPackageDataResponse>()
116+
- MAX_PORTION_DATA_SIZE
117+
- core::mem::size_of_val(&self.portion_of_package_data_len)
118+
+ self.portion_of_package_data_len
119+
{
120+
return Err(PldmCodecError::BufferTooShort);
121+
}
122+
123+
dbg!(core::mem::size_of::<GetPackageDataResponse>());
124+
125+
let mut offset = 0;
126+
buffer[offset..offset + std::mem::size_of_val(&self.hdr.0)].copy_from_slice(&self.hdr.0);
127+
offset += std::mem::size_of_val(&self.hdr.0);
128+
dbg!(offset);
129+
130+
buffer[offset] = self.completion_code;
131+
offset += 1;
132+
133+
buffer[offset..offset + std::mem::size_of_val(&self.next_data_transfer_handle)]
134+
.copy_from_slice(self.next_data_transfer_handle.as_bytes());
135+
offset += std::mem::size_of_val(&self.next_data_transfer_handle);
136+
137+
buffer[offset] = self.transfer_flag;
138+
offset += 1;
139+
140+
buffer[offset..offset + self.portion_of_package_data_len].copy_from_slice(
141+
self.portion_of_package_data[0..self.portion_of_package_data_len].as_bytes(),
142+
);
143+
144+
offset += self.portion_of_package_data_len;
145+
dbg!(&buffer, &buffer.len(), &self.portion_of_package_data_len);
146+
dbg!(self.portion_of_package_data_len);
147+
148+
Ok(offset)
149+
}
150+
151+
fn decode(buffer: &[u8]) -> Result<Self, crate::codec::PldmCodecError> {
152+
const MIN_LEN: usize = core::mem::size_of::<GetPackageDataResponse>()
153+
- core::mem::size_of::<usize>()
154+
- core::mem::size_of::<u8>() * MAX_PORTION_DATA_SIZE;
155+
156+
if buffer.len() < MIN_LEN {
157+
return Err(PldmCodecError::BufferTooShort);
158+
}
159+
160+
let mut offset = 0;
161+
162+
let mut hdr_bytes = [0u8; PLDM_MSG_HEADER_LEN];
163+
hdr_bytes.copy_from_slice(&buffer[offset..offset + PLDM_MSG_HEADER_LEN]);
164+
165+
let hdr = PldmMsgHeader(hdr_bytes);
166+
offset += PLDM_MSG_HEADER_LEN;
167+
168+
let completion_code = GetPackageDataCode::try_from(buffer[offset])
169+
.map_err(|_| PldmCodecError::Unsupported)?;
170+
offset += 1;
171+
172+
let next_data_transfer_handle = u32::from_le_bytes([
173+
buffer[offset],
174+
buffer[offset + 1],
175+
buffer[offset + 2],
176+
buffer[offset + 3],
177+
]);
178+
offset += 4;
179+
180+
let transfer_flag = TransferOperationFlag::try_from(buffer[offset])
181+
.map_err(|_| PldmCodecError::Unsupported)?;
182+
offset += 1;
183+
184+
let portion_len = buffer.len() - offset;
185+
if portion_len > MAX_PORTION_DATA_SIZE {
186+
return Err(PldmCodecError::BufferTooShort);
187+
}
188+
189+
let portion_data = &buffer[offset..];
190+
Ok(Self::new(
191+
hdr.instance_id(),
192+
completion_code,
193+
next_data_transfer_handle,
194+
transfer_flag,
195+
portion_data,
196+
))
197+
}
198+
}
199+
98200
/// The UA sends this command to acquire optional data that the FD shall transfer to the UA prior to
99201
/// beginning the transfer of component images. This command is only used if the FD has indicated in the
100202
/// RequestUpdate command response that it has data that shall be retrieved and restored by the UA. The
@@ -264,11 +366,75 @@ impl<'a> GetMetaDataResponse<'a> {
264366
}
265367
}
266368

369+
impl<'a> PldmCodecWithLifetime<'a> for GetMetaDataResponse<'a> {
370+
fn encode(&self, buffer: &mut [u8]) -> Result<usize, PldmCodecError> {
371+
let size = core::mem::size_of::<Self>() - core::mem::size_of::<&'a [u8]>();
372+
if buffer.len() < size + self.portion_of_device_metadata.len() {
373+
return Err(PldmCodecError::BufferTooShort);
374+
}
375+
376+
let mut offset = 0;
377+
self.hdr
378+
.write_to_prefix(&mut buffer[offset..])
379+
.map_err(|_| PldmCodecError::BufferTooShort)?;
380+
offset += PLDM_MSG_HEADER_LEN;
381+
382+
buffer[offset] = self.completion_code;
383+
offset += 1;
384+
385+
buffer[offset..offset + 4].copy_from_slice(&self.next_data_transfer_handle.to_le_bytes());
386+
offset += 4;
387+
388+
buffer[offset] = self.transfer_flag;
389+
offset += 1;
390+
391+
buffer[offset..offset + self.portion_of_device_metadata.len()]
392+
.copy_from_slice(self.portion_of_device_metadata);
393+
394+
Ok(size)
395+
}
396+
397+
fn decode(buffer: &'a [u8]) -> Result<Self, PldmCodecError> {
398+
let size = core::mem::size_of::<Self>() - core::mem::size_of::<&'a [u8]>();
399+
if buffer.len() < size {
400+
return Err(PldmCodecError::BufferTooShort);
401+
}
402+
403+
let mut offset = 0;
404+
405+
let hdr = PldmMsgHeader::read_from_prefix(&buffer[offset..])
406+
.map_err(|_| PldmCodecError::BufferTooShort)?
407+
.0;
408+
offset += PLDM_MSG_HEADER_LEN;
409+
410+
let completion_code = buffer[offset];
411+
offset += 1;
412+
413+
let next_data_transfer_handle = u32::from_le_bytes(
414+
buffer[offset..offset + 4]
415+
.try_into()
416+
.map_err(|_| PldmCodecError::BufferTooShort)?,
417+
);
418+
offset += 4;
419+
420+
let transfer_flag = buffer[offset];
421+
offset += 1;
422+
423+
let portion_of_device_metadata = &buffer[offset..];
424+
425+
Ok(Self {
426+
hdr,
427+
completion_code,
428+
next_data_transfer_handle,
429+
transfer_flag,
430+
portion_of_device_metadata,
431+
})
432+
}
433+
}
434+
267435
#[cfg(test)]
268436
mod tests {
269437
use super::*;
270-
use crate::protocol;
271-
272438
use crate::codec::PldmCodec;
273439

274440
#[test]
@@ -288,31 +454,69 @@ mod tests {
288454
}
289455

290456
#[test]
291-
fn test_get_data_response() {
457+
fn test_get_package_data_response() {
458+
const PORTION_LEN: usize = 10;
459+
292460
let instance_id: InstanceId = 0x01;
293461
let next_data_transfer_handle: u32 = 0x12345678;
294462
let transfer_operation_flag = TransferOperationFlag::GetFirstPart;
295-
let portion = [0u8; 0xff];
463+
let portion = [22u8; PORTION_LEN];
296464

297-
let _ = GetPackageDataResponse::new(
465+
let resp = GetPackageDataResponse::new(
298466
instance_id,
299467
GetPackageDataCode::BaseCodes(PldmBaseCompletionCode::Success),
300468
next_data_transfer_handle,
301469
transfer_operation_flag,
302470
&portion,
303471
);
304472

305-
//TODO: encoding for response does not work atm due to unknown sizes
306-
// let mut buffer = [0u8; core::mem::size_of::<GetPackageDataResponse>()];
307-
// request.encode(&mut buffer).unwrap();
308-
// let decoded = GetPackageDataResponse::decode(&buffer).unwrap();
473+
let mut buffer_fitted = [0u8; core::mem::size_of::<GetPackageDataResponse>()
474+
- MAX_PORTION_DATA_SIZE
475+
- core::mem::size_of::<usize>()
476+
+ PORTION_LEN];
477+
478+
dbg!(&buffer_fitted.len());
309479

310-
// assert_eq!(request, decoded);
480+
resp.encode(&mut buffer_fitted).unwrap();
481+
let decoded = GetPackageDataResponse::decode(&buffer_fitted).unwrap();
482+
assert_eq!(resp, decoded);
311483
}
312484

313485
#[test]
314-
fn test_get_device_metadata_request() {}
486+
fn test_get_device_metadata_request() {
487+
let instance_id: InstanceId = 0x01;
488+
let data_transfer_handle = 0x12345678;
489+
let req = GetMetaDataRequest::new(
490+
instance_id,
491+
data_transfer_handle,
492+
TransferOperationFlag::GetFirstPart,
493+
);
494+
495+
let mut buffer = [0u8; core::mem::size_of::<GetMetaDataRequest>()];
496+
req.encode(&mut buffer).unwrap();
497+
498+
let decoded = GetMetaDataRequest::decode(&buffer).unwrap();
499+
assert_eq!(req, decoded);
500+
}
315501

316502
#[test]
317-
fn test_get_device_metadata_reponse() {}
503+
fn test_get_device_metadata_response() {
504+
let instance_id: InstanceId = 0x01;
505+
let data_transfer_handle = 0x12345678;
506+
let payload = [11u8; 20];
507+
508+
let resp = GetMetaDataResponse::new(
509+
instance_id,
510+
GetMetaDataCode::BaseCodes(PldmBaseCompletionCode::Success),
511+
data_transfer_handle,
512+
TransferOperationFlag::GetFirstPart,
513+
&payload,
514+
);
515+
516+
let mut buffer = [0u8; 9 + 20];
517+
resp.encode(&mut buffer).unwrap();
518+
519+
let decoded = GetMetaDataResponse::decode(&mut buffer).unwrap();
520+
assert_eq!(resp, decoded);
521+
}
318522
}

src/protocol/base.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,26 @@ macro_rules! pldm_completion_code {
289289
}
290290
}
291291
}
292+
293+
impl TryFrom<u8> for $enum_name {
294+
type Error = crate::error::PldmError;
295+
296+
fn try_from(code: u8) -> Result<Self, Self::Error> {
297+
if let Ok(base_code) = PldmBaseCompletionCode::try_from(code) {
298+
return Ok($enum_name::BaseCodes(base_code));
299+
}
300+
301+
match FwUpdateCompletionCode::try_from(code) {
302+
$(
303+
Ok(FwUpdateCompletionCode::$variant) => {
304+
Ok($enum_name::$variant)
305+
}
306+
)*
307+
Ok(_) => Err(crate::error::PldmError::InvalidCompletionCode),
308+
Err(e) => Err(e),
309+
}
310+
}
311+
}
292312
};
293313
}
294314

0 commit comments

Comments
 (0)