xref: /freebsd/sys/gdb/gdb_packet.c (revision 63518eccca27064285cf2e680510ba9a4c3e2231)
1 /*-
2  * Copyright (c) 2004 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/ctype.h>
33 #include <sys/kdb.h>
34 
35 #include <machine/gdb_machdep.h>
36 
37 #include <gdb/gdb.h>
38 #include <gdb/gdb_int.h>
39 
40 static char gdb_rxbuf[GDB_BUFSZ];
41 char *gdb_rxp = NULL;
42 size_t gdb_rxsz = 0;
43 static char gdb_txbuf[GDB_BUFSZ];
44 char *gdb_txp = NULL;			/* Used in inline functions. */
45 
46 #define	C2N(c)	(((c) < 'A') ? (c) - '0' : \
47 	    10 + (((c) < 'a') ? (c) - 'A' : (c) - 'a'))
48 #define	N2C(n)	(((n) < 10) ? (n) + '0' : (n) + 'a' - 10)
49 
50 /*
51  * Get a single character
52  */
53 
54 static int
55 gdb_getc(void)
56 {
57 	int c;
58 
59 	do
60 		c = gdb_cur->gdb_getc();
61 	while (c == -1);
62 	return (c);
63 }
64 
65 /*
66  * Functions to receive and extract from a packet.
67  */
68 
69 int
70 gdb_rx_begin(void)
71 {
72 	int c, cksum;
73 
74 	gdb_rxp = NULL;
75 	do {
76 		/*
77 		 * Wait for the start character, ignore all others.
78 		 * XXX needs a timeout.
79 		 */
80 		while ((c = gdb_getc()) != '$')
81 			;
82 
83 		/* Read until a # or end of buffer is found. */
84 		cksum = 0;
85 		gdb_rxsz = 0;
86 		while (gdb_rxsz < sizeof(gdb_rxbuf) - 1) {
87 			c = gdb_getc();
88 			if (c == '#')
89 				break;
90 			gdb_rxbuf[gdb_rxsz++] = c;
91 			cksum += c;
92 		}
93 		gdb_rxbuf[gdb_rxsz] = 0;
94 		cksum &= 0xff;
95 
96 		/* Bail out on a buffer overflow. */
97 		if (c != '#') {
98 			gdb_cur->gdb_putc('-');
99 			return (ENOSPC);
100 		}
101 
102 		c = gdb_getc();
103 		cksum -= (C2N(c) << 4) & 0xf0;
104 		c = gdb_getc();
105 		cksum -= C2N(c) & 0x0f;
106 		gdb_cur->gdb_putc((cksum == 0) ? '+' : '-');
107 		if (cksum != 0)
108 			printf("GDB: packet `%s' has invalid checksum\n",
109 			    gdb_rxbuf);
110 	} while (cksum != 0);
111 
112 	gdb_rxp = gdb_rxbuf;
113 	return (0);
114 }
115 
116 int
117 gdb_rx_equal(const char *str)
118 {
119 	int len;
120 
121 	len = strlen(str);
122 	if (len > gdb_rxsz || strncmp(str, gdb_rxp, len) != 0)
123 		return (0);
124 	gdb_rxp += len;
125 	gdb_rxsz -= len;
126 	return (1);
127 }
128 
129 int
130 gdb_rx_mem(unsigned char *addr, size_t size)
131 {
132 	void *prev;
133 	jmp_buf jb;
134 	int ret;
135 	unsigned char c;
136 
137 	if (size * 2 != gdb_rxsz)
138 		return (-1);
139 
140 	prev = kdb_jmpbuf(jb);
141 	ret = setjmp(jb);
142 	if (ret == 0) {
143 		while (size-- > 0) {
144 			c = (C2N(gdb_rxp[0]) << 4) & 0xf0;
145 			c |= C2N(gdb_rxp[1]) & 0x0f;
146 			*addr++ = c;
147 			gdb_rxsz -= 2;
148 			gdb_rxp += 2;
149 		}
150 	}
151 	(void)kdb_jmpbuf(prev);
152 	return ((ret == 0) ? 1 : 0);
153 }
154 
155 int
156 gdb_rx_varhex(uintmax_t *vp)
157 {
158 	uintmax_t v;
159 	int c, neg;
160 
161 	c = gdb_rx_char();
162 	neg = (c == '-') ? 1 : 0;
163 	if (neg == 1)
164 		c = gdb_rx_char();
165 	if (!isxdigit(c)) {
166 		gdb_rxp -= ((c == -1) ? 0 : 1) + neg;
167 		gdb_rxsz += ((c == -1) ? 0 : 1) + neg;
168 		return (-1);
169 	}
170 	v = 0;
171 	do {
172 		v <<= 4;
173 		v += C2N(c);
174 		c = gdb_rx_char();
175 	} while (isxdigit(c));
176 	if (c != -1) {
177 		gdb_rxp--;
178 		gdb_rxsz++;
179 	}
180 	*vp = (neg) ? -v : v;
181 	return (0);
182 }
183 
184 /*
185  * Function to build and send a package.
186  */
187 
188 void
189 gdb_tx_begin(char tp)
190 {
191 
192 	gdb_txp = gdb_txbuf;
193 	if (tp != '\0')
194 		gdb_tx_char(tp);
195 }
196 
197 int
198 gdb_tx_end(void)
199 {
200 	const char *p;
201 	int runlen;
202 	unsigned char c, cksum;
203 
204 	do {
205 		gdb_cur->gdb_putc('$');
206 
207 		cksum = 0;
208 		p = gdb_txbuf;
209 		while (p < gdb_txp) {
210 			/* Send a character and start run-length encoding. */
211 			c = *p++;
212 			gdb_cur->gdb_putc(c);
213 			cksum += c;
214 			runlen = 0;
215 			/* Determine run-length and update checksum. */
216 			while (p < gdb_txp && *p == c) {
217 				runlen++;
218 				p++;
219 			}
220 			/* Emit the run-length encoded string. */
221 			while (runlen >= 97) {
222 				gdb_cur->gdb_putc('*');
223 				cksum += '*';
224 				gdb_cur->gdb_putc(97+29);
225 				cksum += 97+29;
226 				runlen -= 97;
227 				if (runlen > 0) {
228 					gdb_cur->gdb_putc(c);
229 					cksum += c;
230 					runlen--;
231 				}
232 			}
233 			if (runlen == 1) {
234 				gdb_cur->gdb_putc(c);
235 				cksum += c;
236 				runlen--;
237 			}
238 			if (runlen == 0)
239 				continue;
240 			/* Don't emit '$', '#', '+' or '-'. */
241 			if (runlen == 7) {
242 				gdb_cur->gdb_putc(c);
243 				cksum += c;
244 				runlen--;
245 			}
246 			if (runlen == 6 || runlen == 14 || runlen == 16) {
247 				gdb_cur->gdb_putc(c);
248 				cksum += c;
249 				runlen--;
250 			}
251 			gdb_cur->gdb_putc('*');
252 			cksum += '*';
253 			gdb_cur->gdb_putc(runlen+29);
254 			cksum += runlen+29;
255 		}
256 
257 		gdb_cur->gdb_putc('#');
258 		c = cksum >> 4;
259 		gdb_cur->gdb_putc(N2C(c));
260 		c = cksum & 0x0f;
261 		gdb_cur->gdb_putc(N2C(c));
262 
263 		c = gdb_getc();
264 	} while (c != '+');
265 
266 	return (0);
267 }
268 
269 int
270 gdb_tx_mem(const unsigned char *addr, size_t size)
271 {
272 	void *prev;
273 	jmp_buf jb;
274 	int ret;
275 
276 	prev = kdb_jmpbuf(jb);
277 	ret = setjmp(jb);
278 	if (ret == 0) {
279 		while (size-- > 0) {
280 			*gdb_txp++ = N2C(*addr >> 4);
281 			*gdb_txp++ = N2C(*addr & 0x0f);
282 			addr++;
283 		}
284 	}
285 	(void)kdb_jmpbuf(prev);
286 	return ((ret == 0) ? 1 : 0);
287 }
288 
289 void
290 gdb_tx_reg(int regnum)
291 {
292 	unsigned char *regp;
293 	size_t regsz;
294 
295 	regp = gdb_cpu_getreg(regnum, &regsz);
296 	if (regp == NULL) {
297 		/* Register unavailable. */
298 		while (regsz--) {
299 			gdb_tx_char('x');
300 			gdb_tx_char('x');
301 		}
302 	} else
303 		gdb_tx_mem(regp, regsz);
304 }
305