• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::protocol::commands::Command;
2 use crate::protocol::common::hex::decode_hex;
3 use crate::target::Target;
4 
5 /// Packet parse error.
6 #[derive(Debug)]
7 pub enum PacketParseError {
8     ChecksumMismatched { checksum: u8, calculated: u8 },
9     EmptyBuf,
10     MissingChecksum,
11     MalformedChecksum,
12     MalformedCommand,
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 /// Wrapper around a byte buffer containing a GDB packet, while also tracking
25 /// the range of the buffer containing the packet's "body".
26 ///
27 /// A newly constructed `PacketBuf` will have a body that spans the entire data
28 /// portion of the packet (i.e: `b"$data#checksum"`), but this range can be
29 /// further restricted as part of packet parsing.
30 ///
31 /// Notably, `PacketBuf` will _always_ maintain a mutable reference back to the
32 /// _entire_ underlying packet buffer. This makes it possible to re-use any
33 /// unused buffer space as "scratch" space. One notable example of this use-case
34 /// is the 'm' packet, which recycles unused packet buffer space as a buffer for
35 /// the target's `read_memory` method.
36 pub struct PacketBuf<'a> {
37     buf: &'a mut [u8],
38     body_range: core::ops::Range<usize>,
39 }
40 
41 impl<'a> PacketBuf<'a> {
42     /// Validate the contents of the raw packet buffer, checking for checksum
43     /// consistency and structural correctness.
new(pkt_buf: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError>44     pub fn new(pkt_buf: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> {
45         if pkt_buf.is_empty() {
46             return Err(PacketParseError::EmptyBuf);
47         }
48 
49         // split buffer into body and checksum components
50         let mut parts = pkt_buf[1..].split(|b| *b == b'#');
51 
52         let body = parts.next().unwrap(); // spit iter always returns at least one element
53         let checksum = parts
54             .next()
55             .ok_or(PacketParseError::MissingChecksum)?
56             .get(..2)
57             .ok_or(PacketParseError::MalformedChecksum)?;
58 
59         // validate the checksum
60         let checksum = decode_hex(checksum).map_err(|_| PacketParseError::MalformedChecksum)?;
61         let calculated = body.iter().fold(0u8, |a, x| a.wrapping_add(*x));
62         if calculated != checksum {
63             return Err(PacketParseError::ChecksumMismatched {
64                 checksum,
65                 calculated,
66             });
67         }
68 
69         let body_range = 1..(body.len() + 1); // compensate for the leading '$'
70 
71         Ok(PacketBuf {
72             buf: pkt_buf,
73             body_range,
74         })
75     }
76 
77     /// (used for tests) Create a packet buffer from a raw body buffer, skipping
78     /// the header/checksum trimming stage.
79     #[cfg(test)]
new_with_raw_body(body: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError>80     pub fn new_with_raw_body(body: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> {
81         let len = body.len();
82         Ok(PacketBuf {
83             buf: body,
84             body_range: 0..len,
85         })
86     }
87 
88     /// Strip the specified prefix from the packet buffer, returning `true` if
89     /// there was a prefix match.
strip_prefix(&mut self, prefix: &[u8]) -> bool90     pub fn strip_prefix(&mut self, prefix: &[u8]) -> bool {
91         let body = {
92             // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range`
93             // always stays within the bounds of the provided buffer.
94             #[cfg(not(feature = "paranoid_unsafe"))]
95             unsafe {
96                 self.buf.get_unchecked_mut(self.body_range.clone())
97             }
98 
99             #[cfg(feature = "paranoid_unsafe")]
100             &mut self.buf[self.body_range.clone()]
101         };
102 
103         if body.starts_with(prefix) {
104             // SAFETY: if the current buffer range `starts_with` the specified prefix, then
105             // it is safe to bump `body_range.start` by the prefix length.
106             self.body_range = (self.body_range.start + prefix.len())..self.body_range.end;
107             true
108         } else {
109             false
110         }
111     }
112 
113     /// Return a mutable reference to slice of the packet buffer corresponding
114     /// to the current body.
into_body(self) -> &'a mut [u8]115     pub fn into_body(self) -> &'a mut [u8] {
116         // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range`
117         // always stays within the bounds of the provided buffer.
118         #[cfg(not(feature = "paranoid_unsafe"))]
119         unsafe {
120             self.buf.get_unchecked_mut(self.body_range)
121         }
122 
123         #[cfg(feature = "paranoid_unsafe")]
124         &mut self.buf[self.body_range]
125     }
126 
127     /// Return a mutable reference to the _entire_ underlying packet buffer, and
128     /// the current body's range.
into_raw_buf(self) -> (&'a mut [u8], core::ops::Range<usize>)129     pub fn into_raw_buf(self) -> (&'a mut [u8], core::ops::Range<usize>) {
130         (self.buf, self.body_range)
131     }
132 
133     /// Returns the length of the _entire_ underlying packet buffer - not just
134     /// the length of the current range.
135     ///
136     /// This method is used when handing the `qSupported` packet in order to
137     /// obtain the maximum packet size the stub supports.
full_len(&self) -> usize138     pub fn full_len(&self) -> usize {
139         self.buf.len()
140     }
141 }
142 
143 impl<'a> Packet<'a> {
from_buf( target: &mut impl Target, buf: &'a mut [u8], ) -> Result<Packet<'a>, PacketParseError>144     pub fn from_buf(
145         target: &mut impl Target,
146         buf: &'a mut [u8],
147     ) -> Result<Packet<'a>, PacketParseError> {
148         // cannot have empty packet
149         if buf.is_empty() {
150             return Err(PacketParseError::EmptyBuf);
151         }
152 
153         match buf[0] {
154             b'$' => Ok(Packet::Command(
155                 Command::from_packet(target, PacketBuf::new(buf)?)
156                     .ok_or(PacketParseError::MalformedCommand)?,
157             )),
158             b'+' => Ok(Packet::Ack),
159             b'-' => Ok(Packet::Nack),
160             0x03 => Ok(Packet::Interrupt),
161             _ => Err(PacketParseError::UnexpectedHeader(buf[0])),
162         }
163     }
164 }
165