| use crate::protocol::commands::Command; |
| use crate::protocol::common::hex::decode_hex; |
| use crate::target::Target; |
| |
| /// Packet parse error. |
| #[derive(Debug)] |
| pub enum PacketParseError { |
| ChecksumMismatched { checksum: u8, calculated: u8 }, |
| EmptyBuf, |
| MissingChecksum, |
| MalformedChecksum, |
| MalformedCommand, |
| UnexpectedHeader(u8), |
| } |
| |
| /// Top-Level GDB packet |
| pub enum Packet<'a> { |
| Ack, |
| Nack, |
| Interrupt, |
| Command(Command<'a>), |
| } |
| |
| /// Wrapper around a byte buffer containing a GDB packet, while also tracking |
| /// the range of the buffer containing the packet's "body". |
| /// |
| /// A newly constructed `PacketBuf` will have a body that spans the entire data |
| /// portion of the packet (i.e: `b"$data#checksum"`), but this range can be |
| /// further restricted as part of packet parsing. |
| /// |
| /// Notably, `PacketBuf` will _always_ maintain a mutable reference back to the |
| /// _entire_ underlying packet buffer. This makes it possible to re-use any |
| /// unused buffer space as "scratch" space. One notable example of this use-case |
| /// is the 'm' packet, which recycles unused packet buffer space as a buffer for |
| /// the target's `read_memory` method. |
| pub struct PacketBuf<'a> { |
| buf: &'a mut [u8], |
| body_range: core::ops::Range<usize>, |
| } |
| |
| impl<'a> PacketBuf<'a> { |
| /// Validate the contents of the raw packet buffer, checking for checksum |
| /// consistency and structural correctness. |
| pub fn new(pkt_buf: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> { |
| if pkt_buf.is_empty() { |
| return Err(PacketParseError::EmptyBuf); |
| } |
| |
| // split buffer into body and checksum components |
| let mut parts = pkt_buf[1..].split(|b| *b == b'#'); |
| |
| let body = parts.next().unwrap(); // spit iter always returns at least one element |
| let checksum = parts |
| .next() |
| .ok_or(PacketParseError::MissingChecksum)? |
| .get(..2) |
| .ok_or(PacketParseError::MalformedChecksum)?; |
| |
| // validate the checksum |
| let checksum = decode_hex(checksum).map_err(|_| PacketParseError::MalformedChecksum)?; |
| let calculated = body.iter().fold(0u8, |a, x| a.wrapping_add(*x)); |
| if calculated != checksum { |
| return Err(PacketParseError::ChecksumMismatched { |
| checksum, |
| calculated, |
| }); |
| } |
| |
| let body_range = 1..(body.len() + 1); // compensate for the leading '$' |
| |
| Ok(PacketBuf { |
| buf: pkt_buf, |
| body_range, |
| }) |
| } |
| |
| /// (used for tests) Create a packet buffer from a raw body buffer, skipping |
| /// the header/checksum trimming stage. |
| #[cfg(test)] |
| pub fn new_with_raw_body(body: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> { |
| let len = body.len(); |
| Ok(PacketBuf { |
| buf: body, |
| body_range: 0..len, |
| }) |
| } |
| |
| /// Strip the specified prefix from the packet buffer, returning `true` if |
| /// there was a prefix match. |
| pub fn strip_prefix(&mut self, prefix: &[u8]) -> bool { |
| let body = { |
| // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range` |
| // always stays within the bounds of the provided buffer. |
| #[cfg(not(feature = "paranoid_unsafe"))] |
| unsafe { |
| self.buf.get_unchecked_mut(self.body_range.clone()) |
| } |
| |
| #[cfg(feature = "paranoid_unsafe")] |
| &mut self.buf[self.body_range.clone()] |
| }; |
| |
| if body.starts_with(prefix) { |
| // SAFETY: if the current buffer range `starts_with` the specified prefix, then |
| // it is safe to bump `body_range.start` by the prefix length. |
| self.body_range = (self.body_range.start + prefix.len())..self.body_range.end; |
| true |
| } else { |
| false |
| } |
| } |
| |
| /// Return a mutable reference to slice of the packet buffer corresponding |
| /// to the current body. |
| pub fn into_body(self) -> &'a mut [u8] { |
| // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range` |
| // always stays within the bounds of the provided buffer. |
| #[cfg(not(feature = "paranoid_unsafe"))] |
| unsafe { |
| self.buf.get_unchecked_mut(self.body_range) |
| } |
| |
| #[cfg(feature = "paranoid_unsafe")] |
| &mut self.buf[self.body_range] |
| } |
| |
| /// Return a mutable reference to the _entire_ underlying packet buffer, and |
| /// the current body's range. |
| pub fn into_raw_buf(self) -> (&'a mut [u8], core::ops::Range<usize>) { |
| (self.buf, self.body_range) |
| } |
| |
| /// Returns the length of the _entire_ underlying packet buffer - not just |
| /// the length of the current range. |
| /// |
| /// This method is used when handing the `qSupported` packet in order to |
| /// obtain the maximum packet size the stub supports. |
| pub fn full_len(&self) -> usize { |
| self.buf.len() |
| } |
| } |
| |
| impl<'a> Packet<'a> { |
| pub fn from_buf( |
| target: &mut impl Target, |
| buf: &'a mut [u8], |
| ) -> Result<Packet<'a>, PacketParseError> { |
| // cannot have empty packet |
| if buf.is_empty() { |
| return Err(PacketParseError::EmptyBuf); |
| } |
| |
| match buf[0] { |
| b'$' => Ok(Packet::Command( |
| Command::from_packet(target, PacketBuf::new(buf)?) |
| .ok_or(PacketParseError::MalformedCommand)?, |
| )), |
| b'+' => Ok(Packet::Ack), |
| b'-' => Ok(Packet::Nack), |
| 0x03 => Ok(Packet::Interrupt), |
| _ => Err(PacketParseError::UnexpectedHeader(buf[0])), |
| } |
| } |
| } |