1 use crate::protocol::{commands::Command, common::decode_hex}; 2 use crate::target::Target; 3 4 /// Packet parse error. 5 #[derive(Debug)] 6 pub enum PacketParseError { 7 ChecksumMismatched { checksum: u8, calculated: u8 }, 8 EmptyBuf, 9 MissingChecksum, 10 MalformedChecksum, 11 MalformedCommand, 12 NotASCII, 13 UnexpectedHeader(u8), 14 } 15 16 /// Top-Level GDB packet 17 pub enum Packet<'a> { 18 Ack, 19 Nack, 20 Interrupt, 21 Command(Command<'a>), 22 } 23 24 pub struct PacketBuf<'a> { 25 buf: &'a mut [u8], 26 body_range: core::ops::Range<usize>, 27 } 28 29 impl<'a> PacketBuf<'a> { 30 /// Validate the contents of the raw packet buffer, checking for checksum 31 /// consistency, structural correctness, and ASCII validation. new(pkt_buf: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError>32 pub fn new(pkt_buf: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> { 33 // validate the packet is valid ASCII 34 if !pkt_buf.is_ascii() { 35 return Err(PacketParseError::NotASCII); 36 } 37 38 let end_of_body = pkt_buf 39 .iter() 40 .position(|b| *b == b'#') 41 .ok_or(PacketParseError::MissingChecksum)?; 42 43 // split buffer into body and checksum components 44 let (body, checksum) = pkt_buf.split_at_mut(end_of_body); 45 let body = &mut body[1..]; // skip the '$' 46 let checksum = &mut checksum[1..][..2]; // skip the '#' 47 48 // validate the checksum 49 let checksum = decode_hex(checksum).map_err(|_| PacketParseError::MalformedChecksum)?; 50 let calculated = body.iter().fold(0u8, |a, x| a.wrapping_add(*x)); 51 if calculated != checksum { 52 return Err(PacketParseError::ChecksumMismatched { 53 checksum, 54 calculated, 55 }); 56 } 57 58 if log_enabled!(log::Level::Trace) { 59 // SAFETY: body confirmed to be `is_ascii()` 60 let body = unsafe { core::str::from_utf8_unchecked(body) }; 61 trace!("<-- ${}#{:02x?}", body, checksum); 62 } 63 64 Ok(PacketBuf { 65 buf: pkt_buf, 66 body_range: 1..end_of_body, 67 }) 68 } 69 70 /// (used for tests) Create a packet buffer from a raw body buffer, skipping 71 /// the header/checksum trimming stage. ASCII validation is still performed. 72 #[cfg(test)] new_with_raw_body(body: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError>73 pub fn new_with_raw_body(body: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> { 74 // validate the packet is valid ASCII 75 if !body.is_ascii() { 76 return Err(PacketParseError::NotASCII); 77 } 78 79 let len = body.len(); 80 Ok(PacketBuf { 81 buf: body, 82 body_range: 0..len, 83 }) 84 } 85 trim_start_body_bytes(self, n: usize) -> Self86 pub fn trim_start_body_bytes(self, n: usize) -> Self { 87 PacketBuf { 88 buf: self.buf, 89 body_range: (self.body_range.start + n)..self.body_range.end, 90 } 91 } 92 as_body(&'a self) -> &'a [u8]93 pub fn as_body(&'a self) -> &'a [u8] { 94 &self.buf[self.body_range.clone()] 95 } 96 97 /// Return a mut reference to slice of the packet buffer corresponding to 98 /// the current body. into_body(self) -> &'a mut [u8]99 pub fn into_body(self) -> &'a mut [u8] { 100 &mut self.buf[self.body_range] 101 } 102 into_body_str(self) -> &'a str103 pub fn into_body_str(self) -> &'a str { 104 // SAFETY: buffer confirmed to be `is_ascii()` in `new`, and no other PacketBuf 105 // member allow arbitrary modification of `self.buf`. 106 unsafe { core::str::from_utf8_unchecked(&self.buf[self.body_range]) } 107 } 108 109 /// Return a mut reference to the _entire_ underlying packet buffer, and the 110 /// current body's range. 111 #[allow(dead_code)] into_raw_buf(self) -> (&'a mut [u8], core::ops::Range<usize>)112 pub fn into_raw_buf(self) -> (&'a mut [u8], core::ops::Range<usize>) { 113 (self.buf, self.body_range) 114 } 115 } 116 117 impl<'a> Packet<'a> { from_buf( target: &mut impl Target, buf: &'a mut [u8], ) -> Result<Packet<'a>, PacketParseError>118 pub fn from_buf( 119 target: &mut impl Target, 120 buf: &'a mut [u8], 121 ) -> Result<Packet<'a>, PacketParseError> { 122 // cannot have empty packet 123 if buf.is_empty() { 124 return Err(PacketParseError::EmptyBuf); 125 } 126 127 match buf[0] { 128 b'$' => Ok(Packet::Command( 129 Command::from_packet(target, PacketBuf::new(buf)?) 130 // TODO?: preserve command parse error context 131 .map_err(|_| PacketParseError::MalformedCommand)?, 132 )), 133 b'+' => Ok(Packet::Ack), 134 b'-' => Ok(Packet::Nack), 135 0x03 => Ok(Packet::Interrupt), 136 _ => Err(PacketParseError::UnexpectedHeader(buf[0])), 137 } 138 } 139 } 140