2 * GDB remote serial protocol implementation
3 * NetWatch system management mode administration console
5 * Copyright 2009, Google Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
18 * * Neither the name of Google Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived from
20 * this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #define GDB_BUF_SIZE 1024
47 enum fsm_result { NEEDMORE, OK, FAIL };
49 static struct tcp_pcb * last_conn = NULL;
51 void enhexificate (void * ibuf, char * obuf, int len) {
52 unsigned char * cibuf = ibuf;
54 btohex(obuf, *(cibuf++));
62 void rle (char * buf) {
65 int lastchar = buf[0];
69 if (*inptr == lastchar && repcount < 97) {
72 *(outptr++) = lastchar;
75 *(outptr++) = lastchar;
77 while (repcount == 6 || repcount == 7) {
78 *(outptr++) = lastchar;
84 *(outptr++) = repcount + 29;
105 static void close_conn(struct tcp_pcb *pcb, struct gdb_state *state) {
106 outputf("close_conn: bailing");
107 set_run_mode(RM_UNENCUMBERED);
114 outputf("close_conn: done");
117 static int dehexbyte (char * p) {
121 if (v0 < 0 || v1 < 0) return -1;
125 static void finish_and_send(struct tcp_pcb *pcb, char * output_buf, int datalength) {
127 unsigned char checksum = 0;
132 for (p = output_buf + 2; p < output_buf + datalength + 2; p++)
133 checksum += *(unsigned char *)p;
135 output_buf[datalength + 2] = '#';
137 btohex(output_buf + datalength + 3, checksum);
138 tcp_write(pcb, output_buf, datalength + 5, TCP_WRITE_FLAG_COPY);
141 void send_stop_packet() {
142 if (!last_conn) return;
143 tcp_write(last_conn, "$T05thread:01;#07", 17, 0);
146 static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct gdb_state *state) {
149 unsigned char checksum = 0;
154 char output_buf[256];
156 /* Make sure we have at least 4 bytes (the size of the smallest legal
157 packet), and that the packet does in fact start with $. */
159 if ((state->data[0] == '+') || (state->data[0] == '-')) {
160 outputf("GDB: chomp");
165 if (state->writepos < 4) return NEEDMORE;
167 if (state->data[0] != '$') return FAIL;
169 /* Find the end; make sure there's room for the checksum after. */
171 endp = memchr(state->data, '#', state->writepos);
173 if ((!endp) || (endp - state->data > state->writepos - 3))
178 for (p = state->data + 1; p < endp; p++)
179 checksum += *(unsigned char *)p;
181 if (checksum != dehexbyte(endp + 1))
183 outputf("GDB: bad checksum: %d vs %d", checksum , dehexbyte(endp + 1));
187 /* Null-terminate, for processing convenience */
190 outputf("GDB: Got \'%s\'", state->data + 1);
192 /* OK, process the packet */
194 switch (state->data[1]) {
196 tcp_write(pcb, "+$T05thread:01;#07", 18, 0);
199 read_registers_32(output_buf + 2);
200 finish_and_send(pcb, output_buf, 128);
204 /* Parse the address */
205 p = memchr(state->data, ',', endp - state->data);
211 if (!dehexstring(&addr, state->data + 2, -1)) return FAIL;
212 if (!dehexstring(&length, p + 1, -1)) return FAIL;
214 outputf("GDB: read %d from %d", (uint32_t)length, (uint32_t)addr);
216 if (length > 120) length = 120;
218 for (i = 0; i < length; i++) {
221 btohex(output_buf + 2 + (2*i), *(char *)p);
224 finish_and_send(pcb, output_buf, 2*i);
229 set_run_mode(RM_STEPPING);
230 tcp_write(pcb, "+", 1, 0);
234 tcp_write(pcb, "+$#00", 5, 0);
238 state->readpos += (endp - state->data) + 3;
244 gdb_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
245 struct gdb_state *state = arg;
249 outputf("GDB: recv err %d", err);
250 /* FIXME do something better here? */
255 outputf("GDB: Connection closed");
256 close_conn(pcb, state);
260 if (p->tot_len > (GDB_BUF_SIZE - state->writepos)) {
262 outputf("GDB: Overflow!");
263 close_conn(pcb, state);
267 copylen = pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0);
269 outputf("GDB: Processing %d, wp %d, cp %d", p->tot_len, state->writepos, copylen);
271 state->writepos += p->tot_len;
273 tcp_recved(pcb, p->tot_len);
277 switch (recv_fsm(pcb, state)) {
279 outputf("GDB FSM: blocking");
283 if (state->readpos == state->writepos) {
289 state->data + state->readpos,
290 state->writepos - state->readpos);
291 state->writepos -= state->readpos;
297 outputf("GDB: Protocol error");
298 close_conn(pcb, state);
307 static err_t gdb_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) {
309 struct gdb_state *state = arg;
310 send_fsm(pcb, state);
315 static err_t gdb_poll(void *arg, struct tcp_pcb *pcb) {
321 static err_t gdb_accept(void *arg, struct tcp_pcb *pcb, err_t err) {
322 struct gdb_state *state;
324 LWIP_UNUSED_ARG(arg);
325 LWIP_UNUSED_ARG(err);
327 outputf("GDB: accept");
331 set_run_mode(RM_STOPPED);
333 state = (struct gdb_state *)mem_malloc(sizeof(struct gdb_state));
337 outputf("gdb_accept: out of memory\n");
341 memset(state, 0, sizeof(struct gdb_state));
344 tcp_recv(pcb, gdb_recv);
345 tcp_sent(pcb, gdb_sent);
346 tcp_poll(pcb, gdb_poll, 1);
348 tcp_err(pcb, gdb_err);
357 tcp_bind(pcb, IP_ADDR_ANY, GDB_PORT);
358 pcb = tcp_listen(pcb);
359 tcp_accept(pcb, gdb_accept);