mctp_rs/medium/
smbus_espi.rs

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