Skip to content

Commit de17b55

Browse files
committed
fix: QueryDownstreamIdentifiersResponse and Device parsing
Signed-off-by: leongross <leon.gross@9elements.com>
1 parent c32abc4 commit de17b55

File tree

3 files changed

+210
-37
lines changed

3 files changed

+210
-37
lines changed

src/message/firmware_update/query_devid.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ mod test {
213213
use super::*;
214214
use crate::protocol::firmware_update::{Descriptor, DescriptorType};
215215

216-
#[ignore]
216+
//#[ignore]
217217
#[test]
218218
fn test_query_device_identifiers_resp_codec() {
219219
let instance_id = 0;

src/message/firmware_update/query_downstream.rs

Lines changed: 82 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,43 @@ pldm_completion_code! {
129129

130130
#[derive(Debug, Clone, PartialEq)]
131131
#[repr(C)]
132+
/// The total structure for QueryDownstreamIdentifiersResponse looks as follows:
133+
/// ```text
134+
/// QueryDownstreamIdentifiersResponse
135+
/// completion_code (u8)
136+
/// next_data_transfer_handle (u32)
137+
/// transfer_flag (u8)
138+
/// --- portion (variable-length)
139+
/// downstream_devices_length_i (u32)
140+
/// number_of_downstream_devices_i (u16)
141+
/// downstream_devices_index_i (u16)
142+
/// ---
143+
/// downstream_device_index_ij (u16)
144+
/// downstream_descriptor_count_ij (u8)
145+
/// ---
146+
/// descriptor_type_ijk (u16)
147+
/// descriptor_length_ijk (u16)
148+
/// descriptor_data_ijk (variable-length L_ijk)
149+
/// ...
150+
/// descriptor_type_ij(k+1) (u16)
151+
/// descriptor_length_ij(k+1) (u16)
152+
/// descriptor_data_ij(k+1) (variable-length L_ij(k+1))
153+
/// ...
154+
/// ...
155+
/// downstream_devices_index_i(j+1) (u16)
156+
/// downstream_device_count_i(j+1) (u8)
157+
/// ---
158+
/// descriptor_type_(i(j+1)k) (u16)
159+
/// descriptor_length_(i(j+1)k) (u16)
160+
/// descriptor_data_(i(j+1)k) (variable-length L_(i(j+1)k))
161+
/// ...
162+
/// ...
163+
/// downstream_devices_length_(i+1) (u32)
164+
/// number_of_downstream_devices_(i+1) (u16)
165+
/// downstream_devices_index_(i+1) (u16)
166+
/// ...
167+
/// ...
168+
/// ```
132169
pub struct QueryDownstreamIdentifiersResponse<'a> {
133170
pub hdr: PldmMsgHeader<[u8; PLDM_MSG_HEADER_LEN]>,
134171

@@ -227,10 +264,10 @@ impl QueryDownstreamIdentifiersResponse<'_> {
227264
}
228265
}
229266

230-
/// Iterate over all available [DownstreamDevice] in the response portion.
231267
impl<'a> Iterator for QueryDownstreamIdentifiersResponse<'a> {
232268
type Item = DownstreamDevice<'a>;
233269

270+
/// Iterate over all available [DownstreamDevice] in the response portion.
234271
fn next(&mut self) -> Option<Self::Item> {
235272
let portion_hdr = self.try_get_portion_header().ok()?;
236273
if self.portion_iter_current >= portion_hdr.number_of_downstream_devices as usize {
@@ -249,14 +286,18 @@ impl<'a> Iterator for QueryDownstreamIdentifiersResponse<'a> {
249286
return None;
250287
}
251288

252-
let hdr = self
289+
let hdr: DownstreamDevicesHeader = self
253290
.try_get_downstream_device_header(&self.portion[offset..])
254291
.ok()?;
255292
let device_header_size = size_of::<DownstreamDevicesHeader>();
256-
let descriptors_size = hdr.downstream_descriptor_count as usize * size_of::<Descriptor>();
257-
let total_device_size = device_header_size + descriptors_size;
258293

259-
let next_offset = offset + total_device_size;
294+
let desc_size = Descriptor::try_get_descriptor_length_from_blob(
295+
&self.portion[offset + device_header_size..],
296+
hdr.downstream_descriptor_count as usize,
297+
)
298+
.ok()?;
299+
300+
let next_offset = offset + device_header_size + desc_size;
260301
if next_offset > self.portion.len() {
261302
self.portion_iter_current = 0;
262303
self.portion_offset_next = 0;
@@ -423,19 +464,27 @@ impl Iterator for DownstreamDevice<'_> {
423464
return None;
424465
}
425466

426-
let descriptor_size = size_of::<Descriptor>();
427-
if self.downstream_descriptors.len() < descriptor_size {
428-
self._iter_dev_count = 0;
429-
self._iter_offset = 0;
467+
// Read the descriptor length while skipping the type field
468+
let descriptor_length = u16::read_from_bytes(
469+
&self.downstream_descriptors
470+
[self._iter_offset + size_of::<u16>()..self._iter_offset + size_of::<u16>() * 2],
471+
)
472+
.ok()? as usize;
473+
474+
// check bounds
475+
if self._iter_offset + size_of::<u16>() * 2 + descriptor_length
476+
> self.downstream_descriptors.len()
477+
{
430478
return None;
431479
}
432480

433-
let descriptor = Descriptor::try_read_from_prefix(
434-
&self.downstream_descriptors[self._iter_offset..self._iter_offset + descriptor_size],
481+
let descriptor = Descriptor::decode(
482+
&self.downstream_descriptors
483+
[self._iter_offset..self._iter_offset + 2 * size_of::<u16>() + descriptor_length],
435484
)
436-
.ok()?
437-
.0;
438-
self._iter_offset += descriptor_size;
485+
.ok()?;
486+
487+
self._iter_offset += size_of::<u16>() * 2 + descriptor_length;
439488
self._iter_dev_count += 1;
440489

441490
Some(descriptor)
@@ -1141,7 +1190,7 @@ impl RequestDownstreamDeviceUpdateResponse {
11411190
#[cfg(test)]
11421191
mod tests {
11431192
use super::*;
1144-
use crate::codec::PldmCodec;
1193+
use crate::{codec::PldmCodec, protocol::firmware_update::DescriptorType};
11451194

11461195
#[test]
11471196
fn test_query_downstream_devices_request_codec() {
@@ -1389,15 +1438,18 @@ mod tests {
13891438
let descriptors: [Descriptor; 3] = [descriptor.clone(), descriptor.clone(), descriptor];
13901439

13911440
const DESC_LEN: usize = size_of::<Descriptor>();
1392-
let mut descriptor_bytes: [u8; DESC_LEN * 3] = [0u8; DESC_LEN * 3];
1441+
let mut descriptor_bytes_max: [u8; DESC_LEN * 3] = [0u8; DESC_LEN * 3];
13931442
let mut offset = 0;
1443+
1444+
// encoding
13941445
for desc in descriptors.iter() {
1395-
descriptor_bytes[offset..offset + DESC_LEN].copy_from_slice(&desc.as_bytes());
1396-
offset += DESC_LEN;
1446+
let encoded = desc.encode(&mut descriptor_bytes_max[offset..]).unwrap();
1447+
offset += encoded;
13971448
}
13981449

13991450
let downstream_device_index = DownstreamDeviceIndex::try_from(1).unwrap();
1400-
let downstream_device = DownstreamDevice::new(downstream_device_index, &descriptor_bytes);
1451+
let downstream_device =
1452+
DownstreamDevice::new(downstream_device_index, &descriptor_bytes_max);
14011453

14021454
let mut buffer = [0u8; 256];
14031455
let bytes_written = downstream_device.encode(&mut buffer).unwrap();
@@ -1407,6 +1459,7 @@ mod tests {
14071459

14081460
#[test]
14091461
fn test_iterator_query_downstream_identifiers_response() {
1462+
const DSC_DATA_LEN: usize = 16;
14101463
let instance_id: InstanceId = 0x01;
14111464
let ph: PortionHeader = PortionHeader {
14121465
downstream_devices_length: 1,
@@ -1417,22 +1470,21 @@ mod tests {
14171470
downstream_descriptor_count: 3,
14181471
};
14191472
let dsc_0: Descriptor = Descriptor {
1420-
descriptor_type: 0xff,
1421-
descriptor_length: 0x02,
1473+
descriptor_type: DescriptorType::VendorDefined as u16,
1474+
descriptor_length: DSC_DATA_LEN as u16,
14221475
descriptor_data: [0u8; 64],
14231476
};
14241477

14251478
let mut dsc_1 = dsc_0.clone();
1426-
dsc_1.descriptor_type = 0xfe;
1427-
dsc_1.descriptor_data = [1u8; 64];
1479+
dsc_1.descriptor_data[0..16].clone_from_slice(&[1u8; 16]);
14281480

14291481
let mut dsc_2 = dsc_0.clone();
1430-
dsc_2.descriptor_type = 0xfd;
1431-
dsc_2.descriptor_data = [2u8; 64];
1482+
dsc_2.descriptor_data[0..16].clone_from_slice(&[2u8; 16]);
14321483

14331484
const LEN: usize = size_of::<PortionHeader>()
14341485
+ size_of::<DownstreamDevicesHeader>()
1435-
+ 3 * size_of::<Descriptor>();
1486+
+ 3 * (2 * size_of::<u16>() + DSC_DATA_LEN); // type + length + data
1487+
14361488
let mut offset = 0;
14371489
let mut portion: [u8; LEN] = [0u8; LEN];
14381490

@@ -1443,13 +1495,10 @@ mod tests {
14431495
.copy_from_slice(&dsdh.as_bytes());
14441496
offset += size_of::<DownstreamDevicesHeader>();
14451497

1446-
portion[offset..offset + size_of::<Descriptor>()].copy_from_slice(&dsc_0.as_bytes());
1447-
offset += size_of::<Descriptor>();
1448-
1449-
portion[offset..offset + size_of::<Descriptor>()].copy_from_slice(&dsc_1.as_bytes());
1450-
offset += size_of::<Descriptor>();
1451-
1452-
portion[offset..offset + size_of::<Descriptor>()].copy_from_slice(&dsc_2.as_bytes());
1498+
for desc in [dsc_0.clone(), dsc_1.clone(), dsc_2.clone()].iter() {
1499+
let size = &desc.encode(&mut portion[offset..]).unwrap();
1500+
offset += size;
1501+
}
14531502

14541503
let mut qdir = QueryDownstreamIdentifiersResponse::new(
14551504
instance_id,

src/protocol/firmware_update.rs

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::error::PldmError;
55
use bitfield::bitfield;
66
use core::convert::TryFrom;
77
use core::fmt;
8-
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
8+
use zerocopy::{FromBytes, Immutable, IntoBytes};
99

1010
pub const PLDM_FWUP_COMPONENT_RELEASE_DATA_LEN: usize = 8;
1111
pub const PLDM_FWUP_BASELINE_TRANSFER_SIZE: usize = 32;
@@ -285,7 +285,7 @@ pub fn get_descriptor_length(descriptor_type: DescriptorType) -> usize {
285285
}
286286
}
287287

288-
#[derive(Debug, Copy, Clone, PartialEq, FromBytes, IntoBytes, Immutable, KnownLayout)]
288+
#[derive(Debug, Copy, Clone, PartialEq, FromBytes)]
289289
#[repr(C)]
290290
pub struct Descriptor {
291291
pub descriptor_type: u16,
@@ -331,6 +331,112 @@ impl Descriptor {
331331
pub fn codec_size_in_bytes(&self) -> usize {
332332
core::mem::size_of::<u16>() * 2 + self.descriptor_length as usize
333333
}
334+
335+
/// Calculates the total length of a list of descriptors from a blob.
336+
///
337+
/// This simplifies the use case where multiple descriptors are used in a message,
338+
/// and the total length needs to be calculated.
339+
/// It follows the linked list of descriptors by decoding each one in sequence.
340+
///
341+
/// The blob must start with the first descriptor, but may contain additional data after the last descriptor.
342+
///
343+
/// ```rust
344+
/// use pldm_lib::protocol::firmware_update::{Descriptor, DescriptorType, get_descriptor_length};
345+
/// use crate::pldm_lib::codec::PldmCodec;
346+
///
347+
/// let dsc_0 = Descriptor::new(DescriptorType::PciVendorId, &[0x11, 0x22]).unwrap();
348+
/// let dsc_1 = Descriptor::new(DescriptorType::PciVendorId, &[0x33, 0x44]).unwrap();
349+
/// const BLOB_LEN: usize = (size_of::<u16>() * 2 + 2 * size_of::<u8>()) * 2 as usize;
350+
/// let mut blob = [0u8; BLOB_LEN];
351+
///
352+
/// let offset = dsc_0.encode(&mut blob[0..]).unwrap();
353+
/// let _ = dsc_1.encode(&mut blob[offset..]).unwrap();
354+
///
355+
/// let total_len = Descriptor::try_get_descriptor_length_from_blob(&blob, 2).unwrap();
356+
/// assert_eq!(total_len, dsc_0.codec_size_in_bytes() + dsc_1.codec_size_in_bytes());
357+
/// ```
358+
pub fn try_get_descriptor_length_from_blob(
359+
blob: &[u8],
360+
desc_count: usize,
361+
) -> Result<usize, PldmCodecError> {
362+
let mut offset = 0;
363+
let mut total_len = 0;
364+
365+
for _ in 0..desc_count {
366+
let descriptor = Descriptor::decode(&blob[offset..])?;
367+
total_len += descriptor.codec_size_in_bytes();
368+
offset += descriptor.codec_size_in_bytes();
369+
}
370+
371+
Ok(total_len)
372+
}
373+
}
374+
375+
impl PldmCodec for Descriptor {
376+
/// Custom encode implementation of PldmCodec for Descriptor to handle variable-length.
377+
///
378+
/// The descriptor_data field is variable-length, so we need to ensure that only the valid portion
379+
/// of the array is encoded, not all data of the fixed size array of length [DESCRIPTOR_DATA_MAX_LEN].
380+
fn encode(&self, buffer: &mut [u8]) -> Result<usize, PldmCodecError> {
381+
if buffer.len() < self.codec_size_in_bytes() {
382+
return Err(PldmCodecError::BufferTooShort);
383+
}
384+
let mut offset = 0;
385+
386+
self.descriptor_type
387+
.write_to(&mut buffer[offset..offset + core::mem::size_of::<u16>()])
388+
.unwrap();
389+
offset += core::mem::size_of::<u16>();
390+
391+
self.descriptor_length
392+
.write_to(&mut buffer[offset..offset + core::mem::size_of::<u16>()])
393+
.unwrap();
394+
offset += core::mem::size_of::<u16>();
395+
396+
self.descriptor_data[..self.descriptor_length as usize]
397+
.write_to(&mut buffer[offset..offset + self.descriptor_length as usize])
398+
.unwrap();
399+
offset += self.descriptor_length as usize;
400+
401+
Ok(offset)
402+
}
403+
404+
/// Custom decode implementation of PldmCodec for Descriptor to handle variable-length.
405+
///
406+
/// The descriptor_data field is variable-length, so we need to ensure that only the valid portion
407+
/// of the array is decoded, not all data of the fixed size array of length [DESCRIPTOR_DATA_MAX_LEN].
408+
fn decode(buffer: &[u8]) -> Result<Self, PldmCodecError> {
409+
let mut offset = 0;
410+
411+
let descriptor_type = u16::read_from_bytes(
412+
buffer
413+
.get(offset..offset + core::mem::size_of::<u16>())
414+
.ok_or(PldmCodecError::BufferTooShort)?,
415+
)
416+
.unwrap();
417+
offset += core::mem::size_of::<u16>();
418+
419+
let descriptor_length = u16::read_from_bytes(
420+
buffer
421+
.get(offset..offset + core::mem::size_of::<u16>())
422+
.ok_or(PldmCodecError::BufferTooShort)?,
423+
)
424+
.unwrap();
425+
offset += core::mem::size_of::<u16>();
426+
427+
let mut descriptor_data = [0u8; DESCRIPTOR_DATA_MAX_LEN];
428+
descriptor_data[..descriptor_length as usize].copy_from_slice(
429+
buffer
430+
.get(offset..offset + descriptor_length as usize)
431+
.ok_or(PldmCodecError::BufferTooShort)?,
432+
);
433+
434+
Ok(Descriptor {
435+
descriptor_type,
436+
descriptor_length,
437+
descriptor_data,
438+
})
439+
}
334440
}
335441

336442
bitfield! {
@@ -868,10 +974,28 @@ mod test {
868974
descriptor.descriptor_length,
869975
get_descriptor_length(DescriptorType::Uuid) as u16
870976
);
871-
let mut buffer = [0u8; 512];
977+
let mut buffer = [0u8; core::mem::size_of::<Descriptor>()];
872978
descriptor.encode(&mut buffer).unwrap();
979+
873980
let decoded_descriptor = Descriptor::decode(&buffer).unwrap();
874981
assert_eq!(descriptor, decoded_descriptor);
982+
983+
let raw_descriptor = [
984+
0xff, 0xff, // Type=VendorDefined
985+
0x10, 0x00, // size=16
986+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
987+
0x0F, 0x10,
988+
];
989+
let decoded_raw_descriptor = Descriptor::decode(&raw_descriptor).unwrap();
990+
assert_eq!(
991+
decoded_raw_descriptor.descriptor_type,
992+
DescriptorType::VendorDefined as u16,
993+
);
994+
assert_eq!(decoded_raw_descriptor.descriptor_length, 16,);
995+
assert_eq!(
996+
&decoded_raw_descriptor.descriptor_data[..16],
997+
&raw_descriptor[4..20]
998+
);
875999
}
8761000

8771001
#[test]

0 commit comments

Comments
 (0)