Skip to main content

mctp_rs/medium/
smbus_espi.rs

1use bit_register::{NumBytes, TryFromBits, TryIntoBits, bit_register};
2
3use crate::{
4    MctpPacketError,
5    buffer_encoding::{EncodingDecoder, EncodingEncoder, PassthroughEncoding},
6    error::MctpPacketResult,
7    medium::{
8        MctpMedium, MctpMediumFrame,
9        util::{One, Zero},
10    },
11};
12
13#[derive(Debug, Copy, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub struct SmbusEspiMedium;
16
17#[derive(Debug, Copy, Clone, PartialEq, Eq)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub struct SmbusEspiReplyContext {
20    pub destination_slave_address: u8,
21    pub source_slave_address: u8,
22}
23
24impl MctpMedium for SmbusEspiMedium {
25    type Frame = SmbusEspiMediumFrame;
26    type Error = &'static str;
27    type ReplyContext = SmbusEspiReplyContext;
28    type Encoding = PassthroughEncoding;
29
30    fn deserialize<'buf>(
31        &self,
32        packet: &'buf [u8],
33    ) -> MctpPacketResult<(Self::Frame, EncodingDecoder<'buf, Self::Encoding>), Self> {
34        // Check if packet has enough bytes for header
35        if packet.len() < 4 {
36            return Err(MctpPacketError::MediumError(
37                "Packet too short to parse smbus header",
38            ));
39        }
40
41        let header_value =
42            u32::from_be_bytes(packet[0..4].try_into().map_err(|_| {
43                MctpPacketError::MediumError("Packet too short to parse smbus header")
44            })?);
45        // strip off the smbus header
46        let packet = &packet[4..];
47        let header = SmbusEspiMediumHeader::try_from(header_value)
48            .map_err(|_| MctpPacketError::MediumError("Invalid smbus header"))?;
49        if header.byte_count as usize + 1 > packet.len() {
50            return Err(MctpPacketError::MediumError(
51                "Packet too short to parse smbus body and PEC",
52            ));
53        }
54        let pec = packet[header.byte_count as usize];
55        // strip off the PEC byte; the inner stuffed region is the body bytes
56        let inner = &packet[..header.byte_count as usize];
57        Ok((
58            SmbusEspiMediumFrame { header, pec },
59            EncodingDecoder::new(inner),
60        ))
61    }
62
63    fn serialize<'buf, F>(
64        &self,
65        reply_context: Self::ReplyContext,
66        buffer: &'buf mut [u8],
67        message_writer: F,
68    ) -> MctpPacketResult<&'buf [u8], Self>
69    where
70        F: for<'a> FnOnce(&mut EncodingEncoder<'a, Self::Encoding>) -> MctpPacketResult<(), Self>,
71    {
72        // Reserve space for header (4 bytes) and PEC (1 byte)
73        if buffer.len() < 5 {
74            return Err(MctpPacketError::MediumError(
75                "Buffer too small for smbus frame",
76            ));
77        }
78        let buffer_len = buffer.len();
79
80        // Write the body first via an encoder over the body region (reserve
81        // 4 leading header bytes and 1 trailing PEC byte).
82        let body_wire_len = {
83            let body_buf = &mut buffer[4..buffer_len - 1];
84            let mut encoder = EncodingEncoder::<Self::Encoding>::new(body_buf);
85            message_writer(&mut encoder)?;
86            encoder.wire_position()
87        };
88
89        // with the body has been written, construct the header. byte_count
90        // is the number of wire bytes that follow on the line per SMBus
91        // (PassthroughEncoding pairing means wire byte count == decoded
92        // byte count for SMBus today).
93        let header = SmbusEspiMediumHeader {
94            destination_slave_address: reply_context.source_slave_address,
95            source_slave_address: reply_context.destination_slave_address,
96            byte_count: body_wire_len as u8,
97            command_code: SmbusCommandCode::Mctp,
98            ..Default::default()
99        };
100        let header_value =
101            TryInto::<u32>::try_into(header).map_err(MctpPacketError::MediumError)?;
102        buffer[0..4].copy_from_slice(&header_value.to_be_bytes());
103
104        // with the header written, compute the PEC byte
105        let pec_value = smbus_pec::pec(&buffer[0..4 + body_wire_len]);
106        buffer[4 + body_wire_len] = pec_value;
107
108        // add 4 for frame header, add 1 for PEC byte
109        Ok(&buffer[0..4 + body_wire_len + 1])
110    }
111
112    // TODO - this is a guess, need to find the actual value from spec
113    fn max_message_body_size(&self) -> usize {
114        32
115    }
116}
117
118#[repr(u8)]
119#[derive(
120    Debug, Copy, Clone, PartialEq, Eq, num_enum::IntoPrimitive, num_enum::TryFromPrimitive, Default,
121)]
122#[cfg_attr(feature = "defmt", derive(defmt::Format))]
123enum SmbusCommandCode {
124    #[default]
125    Mctp = 0x0F,
126}
127impl TryFromBits<u32> for SmbusCommandCode {
128    fn try_from_bits(bits: u32) -> Result<Self, &'static str> {
129        if bits > 0xFF {
130            Err("Command code out of range")
131        } else {
132            SmbusCommandCode::try_from(bits as u8).map_err(|_| "Invalid command code")
133        }
134    }
135}
136impl TryIntoBits<u32> for SmbusCommandCode {
137    fn try_into_bits(self) -> Result<u32, &'static str> {
138        Ok(Into::<u8>::into(self) as u32)
139    }
140}
141impl NumBytes for SmbusCommandCode {
142    const NUM_BYTES: usize = 1;
143}
144
145// SMBus header per documentation in eSPI spec: https://cdrdv2-public.intel.com/841685/841685_ESPI_IBS_TS_Rev_1_6.pdf
146// See figure 46 on page 74.  This struct corresponds to bytes 3..=6 of the sample OOB MCTP packet
147// frame.
148bit_register! {
149    #[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
150    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
151    struct SmbusEspiMediumHeader: little_endian u32 {
152        pub destination_slave_address: u8 => [25:31],
153        pub _reserved1: Zero => [24],
154        pub command_code: SmbusCommandCode => [16:23],
155        pub byte_count: u8 => [8:15],
156        pub source_slave_address: u8 => [1:7],
157        pub _reserved2: One => [0],
158    }
159}
160
161#[derive(Copy, Clone, PartialEq, Eq, Debug)]
162#[cfg_attr(feature = "defmt", derive(defmt::Format))]
163pub struct SmbusEspiMediumFrame {
164    header: SmbusEspiMediumHeader,
165    pec: u8,
166}
167
168impl SmbusEspiReplyContext {
169    fn new(frame: SmbusEspiMediumFrame) -> Self {
170        Self {
171            destination_slave_address: frame.header.destination_slave_address,
172            source_slave_address: frame.header.source_slave_address,
173        }
174    }
175}
176
177impl MctpMediumFrame<SmbusEspiMedium> for SmbusEspiMediumFrame {
178    fn packet_size(&self) -> usize {
179        self.header.byte_count as usize
180    }
181
182    fn reply_context(&self) -> SmbusEspiReplyContext {
183        SmbusEspiReplyContext::new(*self)
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    extern crate std;
190    use std::vec::Vec;
191
192    use super::*;
193    use crate::buffer_encoding::DecodeError;
194
195    /// Test-only helper: drain an `EncodingDecoder` to a `Vec<u8>` for
196    /// content assertions. Stops at the first error (e.g., `PrematureEnd`).
197    fn drain_to_vec(decoder: &mut EncodingDecoder<'_, PassthroughEncoding>) -> Vec<u8> {
198        let mut out = Vec::new();
199        while let Ok(b) = decoder.read() {
200            out.push(b);
201        }
202        out
203    }
204
205    #[test]
206    fn test_deserialize_valid_packet() {
207        let medium = SmbusEspiMedium;
208
209        // Create a valid SMBus packet with little-endian header
210        // destination_slave_address: 0x20, source_slave_address: 0x10, command: 0x0F, byte_count: 4
211        let header = SmbusEspiMediumHeader {
212            destination_slave_address: 0x20,
213            source_slave_address: 0x10,
214            command_code: SmbusCommandCode::Mctp,
215            byte_count: 4,
216            ..Default::default()
217        };
218        let header_value: u32 = header.try_into().unwrap();
219        let header_bytes = header_value.to_be_bytes();
220
221        let payload = [0xAA, 0xBB, 0xCC, 0xDD]; // 4 bytes as specified by byte_count
222        let mut combined = [0u8; 8];
223        combined[0..4].copy_from_slice(&header_bytes);
224        combined[4..8].copy_from_slice(&payload);
225        let pec = smbus_pec::pec(&combined);
226
227        let mut packet = [0u8; 9];
228        packet[0..4].copy_from_slice(&header_bytes);
229        packet[4..8].copy_from_slice(&payload);
230        packet[8] = pec;
231
232        let result = medium.deserialize(&packet).unwrap();
233        let (frame, mut decoder) = result;
234        let body = drain_to_vec(&mut decoder);
235
236        assert_eq!(frame.header.destination_slave_address, 0x20);
237        assert_eq!(frame.header.source_slave_address, 0x10);
238        assert_eq!(frame.header.command_code, SmbusCommandCode::Mctp);
239        assert_eq!(frame.header.byte_count, 4);
240        assert_eq!(frame.pec, pec);
241        assert_eq!(body, payload);
242    }
243
244    #[test]
245    fn test_deserialize_packet_too_short_header() {
246        let medium = SmbusEspiMedium;
247        let short_packet = [0x01, 0x02]; // Only 2 bytes, need at least 4 for header
248
249        let err = medium.deserialize(&short_packet).err().unwrap();
250        assert_eq!(
251            err,
252            MctpPacketError::MediumError("Packet too short to parse smbus header")
253        );
254    }
255
256    #[test]
257    fn test_deserialize_packet_too_short_body() {
258        let medium = SmbusEspiMedium;
259
260        // Header indicates 10 bytes of data but we only provide 2
261        let header_bytes = [
262            0x20, // destination_slave_address
263            0x0F, // command_code (MCTP)
264            0x0A, // byte_count: 10 bytes
265            0x21, // source_slave_address
266        ];
267
268        let short_payload = [0xAA, 0xBB]; // Only 2 bytes, but header says 10
269
270        let mut packet = [0u8; 6];
271        packet[0..4].copy_from_slice(&header_bytes);
272        packet[4..6].copy_from_slice(&short_payload);
273
274        let err = medium.deserialize(&packet).err().unwrap();
275        assert_eq!(
276            err,
277            MctpPacketError::MediumError("Packet too short to parse smbus body and PEC")
278        );
279    }
280
281    #[test]
282    fn test_deserialize_invalid_header() {
283        let medium = SmbusEspiMedium;
284
285        // Create invalid header with command code that's not MCTP
286        let invalid_header_bytes = [
287            0x20, // destination_slave_address
288            0xFF, // invalid command_code (not 0x0F)
289            0x04, // byte_count
290            0x20, // source_slave_address
291        ];
292
293        let payload = [0xAA, 0xBB, 0xCC, 0xDD];
294        let pec = 0x00; // PEC doesn't matter for this test
295
296        let mut packet = [0u8; 9];
297        packet[0..4].copy_from_slice(&invalid_header_bytes);
298        packet[4..8].copy_from_slice(&payload);
299        packet[8] = pec;
300
301        let err = medium.deserialize(&packet).err().unwrap();
302        assert_eq!(err, MctpPacketError::MediumError("Invalid smbus header"));
303    }
304
305    #[test]
306    fn test_deserialize_zero_byte_count() {
307        let medium = SmbusEspiMedium;
308
309        let header_bytes = [
310            0x20, // destination_slave_address
311            0x0F, // command_code (MCTP)
312            0x00, // byte_count: 0 bytes
313            0x21, // source_slave_address
314        ];
315
316        let pec = smbus_pec::pec(&header_bytes);
317
318        let mut packet = [0u8; 5];
319        packet[0..4].copy_from_slice(&header_bytes);
320        packet[4] = pec;
321
322        let result = medium.deserialize(&packet).unwrap();
323        let (frame, mut decoder) = result;
324
325        assert_eq!(frame.header.byte_count, 0);
326        assert_eq!(frame.pec, pec);
327        assert_eq!(decoder.read().unwrap_err(), DecodeError::PrematureEnd);
328    }
329
330    #[test]
331    fn test_serialize_valid_packet() {
332        let medium = SmbusEspiMedium;
333        let reply_context = SmbusEspiReplyContext {
334            destination_slave_address: 0x20,
335            source_slave_address: 0x10,
336        };
337
338        let mut buffer = [0u8; 64];
339        let test_payload = [0xAA, 0xBB, 0xCC, 0xDD];
340
341        let result = medium
342            .serialize(reply_context, &mut buffer, |encoder| {
343                encoder
344                    .write_all(&test_payload)
345                    .map_err(|_| MctpPacketError::SerializeError("encode error"))
346            })
347            .unwrap();
348
349        // Verify the serialized packet structure
350        // Header: 4 bytes + payload: 4 bytes + PEC: 1 byte = 9 bytes total
351        assert_eq!(result.len(), 9);
352
353        // Parse the header to verify correctness
354        let header_value = u32::from_be_bytes([result[0], result[1], result[2], result[3]]);
355        let header = SmbusEspiMediumHeader::try_from(header_value).unwrap();
356
357        // Note: destination and source are swapped in reply
358        assert_eq!(header.destination_slave_address, 0x10); // reply_context.source
359        assert_eq!(header.source_slave_address, 0x20); // reply_context.destination
360        assert_eq!(header.command_code, SmbusCommandCode::Mctp);
361        assert_eq!(header.byte_count, 4);
362
363        // Verify payload
364        assert_eq!(&result[4..8], &test_payload);
365
366        // Verify PEC byte
367        let expected_pec = smbus_pec::pec(&result[0..8]);
368        assert_eq!(result[8], expected_pec);
369    }
370
371    #[test]
372    fn test_serialize_buffer_too_small() {
373        let medium = SmbusEspiMedium;
374        let reply_context = SmbusEspiReplyContext {
375            destination_slave_address: 0x20,
376            source_slave_address: 0x10,
377        };
378
379        let mut small_buffer = [0u8; 4]; // Only 4 bytes, need at least 5 (header + PEC)
380
381        let err = medium
382            .serialize(reply_context, &mut small_buffer, |_| Ok(()))
383            .err()
384            .unwrap();
385
386        assert_eq!(
387            err,
388            MctpPacketError::MediumError("Buffer too small for smbus frame")
389        );
390    }
391
392    #[test]
393    fn test_serialize_minimal_buffer() {
394        let medium = SmbusEspiMedium;
395        let reply_context = SmbusEspiReplyContext {
396            destination_slave_address: 0x20,
397            source_slave_address: 0x10,
398        };
399
400        let mut minimal_buffer = [0u8; 5]; // Exactly 5 bytes (4 header + 1 PEC)
401
402        let result = medium
403            .serialize(
404                reply_context,
405                &mut minimal_buffer,
406                |_| Ok(()), // No payload data
407            )
408            .unwrap();
409
410        assert_eq!(result.len(), 5);
411
412        // Verify header
413        let header_value = u32::from_be_bytes([result[0], result[1], result[2], result[3]]);
414        let header = SmbusEspiMediumHeader::try_from(header_value).unwrap();
415        assert_eq!(header.byte_count, 0);
416
417        // Verify PEC
418        let expected_pec = smbus_pec::pec(&result[0..4]);
419        assert_eq!(result[4], expected_pec);
420    }
421
422    #[test]
423    fn test_serialize_max_payload() {
424        let medium = SmbusEspiMedium;
425        let reply_context = SmbusEspiReplyContext {
426            destination_slave_address: 0x20,
427            source_slave_address: 0x10,
428        };
429
430        // Test with maximum payload size (255 bytes as byte_count is u8)
431        let max_payload = [0x55u8; 255];
432        let mut buffer = [0u8; 260]; // 4 + 255 + 1 = header + max payload + PEC
433
434        let result = medium
435            .serialize(reply_context, &mut buffer, |encoder| {
436                encoder
437                    .write_all(&max_payload)
438                    .map_err(|_| MctpPacketError::SerializeError("encode error"))
439            })
440            .unwrap();
441
442        assert_eq!(result.len(), 260); // 4 + 255 + 1
443
444        // Verify header
445        let header_value = u32::from_be_bytes([result[0], result[1], result[2], result[3]]);
446        let header = SmbusEspiMediumHeader::try_from(header_value).unwrap();
447        assert_eq!(header.byte_count, 255);
448
449        // Verify payload
450        assert_eq!(&result[4..259], &max_payload[..]);
451
452        // Verify PEC
453        let expected_pec = smbus_pec::pec(&result[0..259]);
454        assert_eq!(result[259], expected_pec);
455    }
456
457    #[test]
458    fn test_serialize_message_writer_error() {
459        let medium = SmbusEspiMedium;
460        let reply_context = SmbusEspiReplyContext {
461            destination_slave_address: 0x20,
462            source_slave_address: 0x10,
463        };
464
465        let mut buffer = [0u8; 64];
466
467        let result = medium.serialize(reply_context, &mut buffer, |_| {
468            Err(MctpPacketError::MediumError("Test error"))
469        });
470
471        assert_eq!(result, Err(MctpPacketError::MediumError("Test error")));
472    }
473
474    #[test]
475    fn test_roundtrip_serialization_deserialization() {
476        let medium = SmbusEspiMedium;
477        let original_context = SmbusEspiReplyContext {
478            destination_slave_address: 0x42,
479            source_slave_address: 0x24,
480        };
481
482        let original_payload = [0x11, 0x22, 0x33, 0x44, 0x55];
483        let mut buffer = [0u8; 64];
484
485        // Serialize
486        let serialized = medium
487            .serialize(original_context, &mut buffer, |encoder| {
488                encoder
489                    .write_all(&original_payload)
490                    .map_err(|_| MctpPacketError::SerializeError("encode error"))
491            })
492            .unwrap();
493
494        // Deserialize
495        let (frame, mut decoder) = medium.deserialize(serialized).unwrap();
496        let deserialized_payload = drain_to_vec(&mut decoder);
497
498        // Verify roundtrip correctness
499        assert_eq!(deserialized_payload, original_payload);
500        assert_eq!(frame.header.destination_slave_address, 0x24); // swapped
501        assert_eq!(frame.header.source_slave_address, 0x42); // swapped
502        assert_eq!(frame.header.command_code, SmbusCommandCode::Mctp);
503        assert_eq!(frame.header.byte_count, original_payload.len() as u8);
504
505        // Verify PEC is correct
506        let expected_pec = smbus_pec::pec(&serialized[0..serialized.len() - 1]);
507        assert_eq!(frame.pec, expected_pec);
508    }
509
510    #[test]
511    fn test_frame_packet_size() {
512        let frame = SmbusEspiMediumFrame {
513            header: SmbusEspiMediumHeader {
514                byte_count: 42,
515                ..Default::default()
516            },
517            pec: 0,
518        };
519
520        assert_eq!(frame.packet_size(), 42);
521    }
522
523    #[test]
524    fn test_frame_reply_context() {
525        let frame = SmbusEspiMediumFrame {
526            header: SmbusEspiMediumHeader {
527                destination_slave_address: 0x30,
528                source_slave_address: 0x40,
529                ..Default::default()
530            },
531            pec: 0,
532        };
533
534        let context = frame.reply_context();
535        assert_eq!(context.destination_slave_address, 0x30);
536        assert_eq!(context.source_slave_address, 0x40);
537    }
538
539    #[test]
540    fn test_smbus_command_code_conversion() {
541        // Test valid command code
542        assert_eq!(
543            SmbusCommandCode::try_from_bits(0x0F).unwrap(),
544            SmbusCommandCode::Mctp
545        );
546
547        // Test out of range (> 0xFF)
548        assert_eq!(
549            SmbusCommandCode::try_from_bits(0x100),
550            Err("Command code out of range")
551        );
552
553        // Test invalid command code
554        assert_eq!(
555            SmbusCommandCode::try_from_bits(0x10),
556            Err("Invalid command code")
557        );
558
559        // Test conversion to bits
560        assert_eq!(SmbusCommandCode::Mctp.try_into_bits().unwrap(), 0x0F);
561    }
562
563    #[test]
564    fn test_header_bit_register_edge_cases() {
565        // Test all zeros - this should use default command code
566        let header = SmbusEspiMediumHeader::default();
567        assert_eq!(header.destination_slave_address, 0);
568        assert_eq!(header.source_slave_address, 0);
569        assert_eq!(header.byte_count, 0);
570        assert_eq!(header.command_code, SmbusCommandCode::Mctp); // default
571
572        // Test valid maximum values within bit ranges
573        let header = SmbusEspiMediumHeader {
574            destination_slave_address: 0x7F, // 7 bits max (bits 25-31)
575            source_slave_address: 0x3F,      // 6 bits max (bits 1-7, bit 0 reserved)
576            byte_count: 0xFF,                // 8 bits max (bits 8-15)
577            command_code: SmbusCommandCode::Mctp,
578            ..Default::default()
579        };
580
581        // Verify we can convert to u32 and back
582        let header_value: u32 = header.try_into().unwrap();
583        let reconstructed = SmbusEspiMediumHeader::try_from(header_value).unwrap();
584        assert_eq!(reconstructed, header);
585    }
586
587    #[test]
588    fn test_pec_calculation_accuracy() {
589        let medium = SmbusEspiMedium;
590        let reply_context = SmbusEspiReplyContext {
591            destination_slave_address: 0x50,
592            source_slave_address: 0x30,
593        };
594
595        // Test with known data to verify PEC calculation
596        let test_data = [0x01, 0x02, 0x03];
597        let mut buffer = [0u8; 32];
598
599        let result = medium
600            .serialize(reply_context, &mut buffer, |encoder| {
601                encoder
602                    .write_all(&test_data)
603                    .map_err(|_| MctpPacketError::SerializeError("encode error"))
604            })
605            .unwrap();
606
607        // Manually calculate expected PEC and compare
608        let data_for_pec = &result[0..result.len() - 1];
609        let expected_pec = smbus_pec::pec(data_for_pec);
610        let actual_pec = result[result.len() - 1];
611
612        assert_eq!(actual_pec, expected_pec);
613    }
614
615    #[test]
616    fn test_serialize_with_empty_payload() {
617        let medium = SmbusEspiMedium;
618        let reply_context = SmbusEspiReplyContext {
619            destination_slave_address: 0x60,
620            source_slave_address: 0x70,
621        };
622
623        let mut buffer = [0u8; 16];
624
625        let result = medium
626            .serialize(
627                reply_context,
628                &mut buffer,
629                |_| Ok(()), // Empty payload
630            )
631            .unwrap();
632
633        assert_eq!(result.len(), 5); // 4 bytes header + 1 byte PEC
634
635        // Verify header
636        let header_value = u32::from_be_bytes([result[0], result[1], result[2], result[3]]);
637        let header = SmbusEspiMediumHeader::try_from(header_value).unwrap();
638        assert_eq!(header.byte_count, 0);
639        assert_eq!(header.destination_slave_address, 0x70); // swapped
640        assert_eq!(header.source_slave_address, 0x60); // swapped
641
642        // Verify PEC
643        let expected_pec = smbus_pec::pec(&result[0..4]);
644        assert_eq!(result[4], expected_pec);
645    }
646
647    #[test]
648    fn test_max_message_body_size() {
649        let medium = SmbusEspiMedium;
650        assert_eq!(medium.max_message_body_size(), 32);
651    }
652
653    #[test]
654    fn test_address_swapping_in_reply_context() {
655        // Test that addresses are properly swapped when creating reply context
656        let original_frame = SmbusEspiMediumFrame {
657            header: SmbusEspiMediumHeader {
658                destination_slave_address: 0x2A, // Valid 7-bit address
659                source_slave_address: 0x3B,      // Valid 6-bit address
660                ..Default::default()
661            },
662            pec: 0,
663        };
664
665        let reply_context = SmbusEspiReplyContext::new(original_frame);
666        assert_eq!(reply_context.destination_slave_address, 0x2A);
667        assert_eq!(reply_context.source_slave_address, 0x3B);
668
669        // Now test that when we serialize with this context, addresses are swapped back
670        let medium = SmbusEspiMedium;
671        let mut buffer = [0u8; 16];
672
673        let result = medium
674            .serialize(reply_context, &mut buffer, |_| Ok(()))
675            .unwrap();
676
677        let header_value = u32::from_be_bytes([result[0], result[1], result[2], result[3]]);
678        let response_header = SmbusEspiMediumHeader::try_from(header_value).unwrap();
679
680        // In the response, source becomes destination and vice versa
681        assert_eq!(response_header.destination_slave_address, 0x3B);
682        assert_eq!(response_header.source_slave_address, 0x2A);
683    }
684
685    #[test]
686    fn test_deserialize_with_different_byte_counts() {
687        let medium = SmbusEspiMedium;
688
689        for byte_count in [1, 16, 32, 64, 128, 255] {
690            let header_bytes = [
691                0x20,       // destination_slave_address
692                0x0F,       // command_code (MCTP)
693                byte_count, // byte_count
694                0x21,       // source_slave_address
695            ];
696
697            let payload = [0x42u8; 255];
698            let payload_slice = &payload[..byte_count as usize];
699
700            let mut combined = [0u8; 259]; // 4 header + 255 max payload
701            combined[0..4].copy_from_slice(&header_bytes);
702            combined[4..4 + byte_count as usize].copy_from_slice(payload_slice);
703            let pec = smbus_pec::pec(&combined[0..4 + byte_count as usize]);
704
705            let mut packet = [0u8; 260]; // 4 + 255 + 1
706            packet[0..4].copy_from_slice(&header_bytes);
707            packet[4..4 + byte_count as usize].copy_from_slice(payload_slice);
708            packet[4 + byte_count as usize] = pec;
709
710            let packet_slice = &packet[0..4 + byte_count as usize + 1];
711            let result = medium.deserialize(packet_slice).unwrap();
712            let (frame, mut decoder) = result;
713            let body = drain_to_vec(&mut decoder);
714
715            assert_eq!(frame.header.byte_count, byte_count);
716            assert_eq!(body.len(), byte_count as usize);
717            assert_eq!(frame.pec, pec);
718        }
719    }
720
721    #[test]
722    fn test_smbus_buffer_overflow_protection() {
723        let medium = SmbusEspiMedium;
724
725        // Test packet with byte_count that would cause overflow
726        let header_bytes = [
727            0x20, // destination_slave_address
728            0x0F, // command_code (MCTP)
729            0xFF, // byte_count: 255 bytes (maximum)
730            0x21, // source_slave_address
731        ];
732
733        // Provide a packet that's too short for the claimed byte_count
734        let short_payload = [0xAA, 0xBB]; // Only 2 bytes, but header claims 255
735        let mut packet = [0u8; 7]; // 4 header + 2 payload + 1 PEC = 7 total
736        packet[0..4].copy_from_slice(&header_bytes);
737        packet[4..6].copy_from_slice(&short_payload);
738        packet[6] = 0x00; // PEC (doesn't matter for this test)
739
740        let err = medium.deserialize(&packet).err().unwrap();
741        assert_eq!(
742            err,
743            MctpPacketError::MediumError("Packet too short to parse smbus body and PEC")
744        );
745    }
746
747    #[test]
748    fn test_smbus_serialize_buffer_underflow() {
749        let medium = SmbusEspiMedium;
750        let reply_context = SmbusEspiReplyContext {
751            destination_slave_address: 0x20,
752            source_slave_address: 0x10,
753        };
754
755        // Test with buffer smaller than minimum required (4 header + 1 PEC = 5 bytes)
756        let mut tiny_buffer = [0u8; 4]; // Only 4 bytes, need at least 5
757
758        let err = medium
759            .serialize(reply_context, &mut tiny_buffer, |_| {
760                Ok(()) // No payload
761            })
762            .err()
763            .unwrap();
764
765        assert_eq!(
766            err,
767            MctpPacketError::MediumError("Buffer too small for smbus frame")
768        );
769    }
770
771    #[test]
772    fn test_smbus_header_bounds_checking() {
773        let medium = SmbusEspiMedium;
774
775        // Test with packet shorter than header size (4 bytes)
776        for packet_size in 0..4 {
777            let short_packet = [0u8; 4];
778            let err = medium
779                .deserialize(&short_packet[..packet_size])
780                .err()
781                .unwrap();
782            assert_eq!(
783                err,
784                MctpPacketError::MediumError("Packet too short to parse smbus header")
785            );
786        }
787    }
788
789    #[test]
790    fn test_smbus_pec_bounds_checking() {
791        let medium = SmbusEspiMedium;
792
793        // Test with packet that has header but claims more data than available for PEC
794        let header_bytes = [
795            0x20, // destination_slave_address
796            0x0F, // command_code (MCTP)
797            0x05, // byte_count: 5 bytes
798            0x21, // source_slave_address
799        ];
800
801        // Provide exactly enough bytes for the data but no PEC byte
802        let payload = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE]; // 5 bytes as claimed
803        let mut packet = [0u8; 9]; // 4 header + 5 payload = 9 total (missing PEC)
804        packet[0..4].copy_from_slice(&header_bytes);
805        packet[4..9].copy_from_slice(&payload);
806
807        let err = medium.deserialize(&packet).err().unwrap();
808        assert_eq!(
809            err,
810            MctpPacketError::MediumError("Packet too short to parse smbus body and PEC")
811        );
812    }
813
814    #[test]
815    fn test_smbus_zero_byte_count_edge_case() {
816        let medium = SmbusEspiMedium;
817
818        // Test with zero byte count but packet shorter than header + PEC
819        let header_bytes = [
820            0x20, // destination_slave_address
821            0x0F, // command_code (MCTP)
822            0x00, // byte_count: 0 bytes
823            0x21, // source_slave_address
824        ];
825
826        // Test with packet missing PEC byte
827        let mut short_packet = [0u8; 4]; // Only header, no PEC
828        short_packet.copy_from_slice(&header_bytes);
829
830        let err = medium.deserialize(&short_packet).err().unwrap();
831        assert_eq!(
832            err,
833            MctpPacketError::MediumError("Packet too short to parse smbus body and PEC")
834        );
835    }
836
837    #[test]
838    fn test_smbus_maximum_payload_boundary() {
839        let medium = SmbusEspiMedium;
840
841        // Test serialization at the boundary of maximum payload (255 bytes)
842        let reply_context = SmbusEspiReplyContext {
843            destination_slave_address: 0x20,
844            source_slave_address: 0x10,
845        };
846
847        let max_payload = [0x55u8; 255];
848        let mut buffer = [0u8; 260]; // 4 + 255 + 1 = exactly enough
849
850        let result = medium.serialize(reply_context, &mut buffer, |encoder| {
851            encoder
852                .write_all(&max_payload)
853                .map_err(|_| MctpPacketError::SerializeError("encode error"))
854        });
855
856        assert!(result.is_ok());
857        let serialized = result.unwrap();
858        assert_eq!(serialized.len(), 260); // Should use exactly all available space
859
860        // Test with buffer one byte too small for maximum payload.
861        // The encoder will hit BufferFull when trying to write the
862        // 255th payload byte (only 254 fit after header reservation),
863        // so this serialize call now returns an error rather than
864        // silently truncating.
865        let mut small_buffer = [0u8; 259]; // One byte short for max payload
866        let result_small = medium.serialize(reply_context, &mut small_buffer, |encoder| {
867            encoder
868                .write_all(&max_payload)
869                .map_err(|_| MctpPacketError::SerializeError("encode error"))
870        });
871
872        assert_eq!(
873            result_small.err().unwrap(),
874            MctpPacketError::SerializeError("encode error")
875        );
876    }
877}