mctp_rs/medium/
smbus_espi.rs

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