xref: /freebsd/sys/gdb/gdb_packet.c (revision 1c05a6ea6b849ff95e539c31adea887c644a6a01)
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 #include <sys/libkern.h>
35 #include <sys/ttydefaults.h>
36 
37 #include <machine/gdb_machdep.h>
38 #include <machine/kdb.h>
39 
40 #include <gdb/gdb.h>
41 #include <gdb/gdb_int.h>
42 
43 static char gdb_rxbuf[GDB_BUFSZ];
44 char *gdb_rxp = NULL;
45 size_t gdb_rxsz = 0;
46 static char gdb_txbuf[GDB_BUFSZ];
47 char *gdb_txp = NULL;			/* Used in inline functions. */
48 
49 #define	C2N(c)	(((c) < 'A') ? (c) - '0' : \
50 	    10 + (((c) < 'a') ? (c) - 'A' : (c) - 'a'))
51 #define	N2C(n)	(((n) < 10) ? (n) + '0' : (n) + 'a' - 10)
52 
53 /*
54  * Get a single character
55  */
56 
57 static int
58 gdb_getc(void)
59 {
60 	int c;
61 
62 	do
63 		c = gdb_cur->gdb_getc();
64 	while (c == -1);
65 
66 	if (c == CTRL('C')) {
67 		printf("Received ^C; trying to switch back to ddb.\n");
68 
69 		if (kdb_dbbe_select("ddb") != 0)
70 			printf("The ddb backend could not be selected.\n");
71 		else {
72 			printf("using longjmp, hope it works!\n");
73 			kdb_reenter();
74 		}
75 	}
76 	return (c);
77 }
78 
79 /*
80  * Functions to receive and extract from a packet.
81  */
82 
83 int
84 gdb_rx_begin(void)
85 {
86 	int c, cksum;
87 
88 	gdb_rxp = NULL;
89 	do {
90 		/*
91 		 * Wait for the start character, ignore all others.
92 		 * XXX needs a timeout.
93 		 */
94 		while ((c = gdb_getc()) != '$')
95 			;
96 
97 		/* Read until a # or end of buffer is found. */
98 		cksum = 0;
99 		gdb_rxsz = 0;
100 		while (gdb_rxsz < sizeof(gdb_rxbuf) - 1) {
101 			c = gdb_getc();
102 			if (c == '#')
103 				break;
104 			gdb_rxbuf[gdb_rxsz++] = c;
105 			cksum += c;
106 		}
107 		gdb_rxbuf[gdb_rxsz] = 0;
108 		cksum &= 0xff;
109 
110 		/* Bail out on a buffer overflow. */
111 		if (c != '#') {
112 			gdb_cur->gdb_putc('-');
113 			return (ENOSPC);
114 		}
115 
116 		c = gdb_getc();
117 		cksum -= (C2N(c) << 4) & 0xf0;
118 		c = gdb_getc();
119 		cksum -= C2N(c) & 0x0f;
120 		gdb_cur->gdb_putc((cksum == 0) ? '+' : '-');
121 		if (cksum != 0)
122 			printf("GDB: packet `%s' has invalid checksum\n",
123 			    gdb_rxbuf);
124 	} while (cksum != 0);
125 
126 	gdb_rxp = gdb_rxbuf;
127 	return (0);
128 }
129 
130 int
131 gdb_rx_equal(const char *str)
132 {
133 	int len;
134 
135 	len = strlen(str);
136 	if (len > gdb_rxsz || strncmp(str, gdb_rxp, len) != 0)
137 		return (0);
138 	gdb_rxp += len;
139 	gdb_rxsz -= len;
140 	return (1);
141 }
142 
143 int
144 gdb_rx_mem(unsigned char *addr, size_t size)
145 {
146 	unsigned char *p;
147 	void *prev;
148 	jmp_buf jb;
149 	size_t cnt;
150 	int ret;
151 	unsigned char c;
152 
153 	if (size * 2 != gdb_rxsz)
154 		return (-1);
155 
156 	prev = kdb_jmpbuf(jb);
157 	ret = setjmp(jb);
158 	if (ret == 0) {
159 		p = addr;
160 		cnt = size;
161 		while (cnt-- > 0) {
162 			c = (C2N(gdb_rxp[0]) << 4) & 0xf0;
163 			c |= C2N(gdb_rxp[1]) & 0x0f;
164 			*p++ = c;
165 			gdb_rxsz -= 2;
166 			gdb_rxp += 2;
167 		}
168 		kdb_cpu_sync_icache(addr, size);
169 	}
170 	(void)kdb_jmpbuf(prev);
171 	return ((ret == 0) ? 1 : 0);
172 }
173 
174 int
175 gdb_rx_varhex(uintmax_t *vp)
176 {
177 	uintmax_t v;
178 	int c, neg;
179 
180 	c = gdb_rx_char();
181 	neg = (c == '-') ? 1 : 0;
182 	if (neg == 1)
183 		c = gdb_rx_char();
184 	if (!isxdigit(c)) {
185 		gdb_rxp -= ((c == -1) ? 0 : 1) + neg;
186 		gdb_rxsz += ((c == -1) ? 0 : 1) + neg;
187 		return (-1);
188 	}
189 	v = 0;
190 	do {
191 		v <<= 4;
192 		v += C2N(c);
193 		c = gdb_rx_char();
194 	} while (isxdigit(c));
195 	if (c != -1) {
196 		gdb_rxp--;
197 		gdb_rxsz++;
198 	}
199 	*vp = (neg) ? -v : v;
200 	return (0);
201 }
202 
203 /*
204  * Function to build and send a package.
205  */
206 
207 void
208 gdb_tx_begin(char tp)
209 {
210 
211 	gdb_txp = gdb_txbuf;
212 	if (tp != '\0')
213 		gdb_tx_char(tp);
214 }
215 
216 int
217 gdb_tx_end(void)
218 {
219 	const char *p;
220 	int runlen;
221 	unsigned char c, cksum;
222 
223 	do {
224 		gdb_cur->gdb_putc('$');
225 
226 		cksum = 0;
227 		p = gdb_txbuf;
228 		while (p < gdb_txp) {
229 			/* Send a character and start run-length encoding. */
230 			c = *p++;
231 			gdb_cur->gdb_putc(c);
232 			cksum += c;
233 			runlen = 0;
234 			/* Determine run-length and update checksum. */
235 			while (p < gdb_txp && *p == c) {
236 				runlen++;
237 				p++;
238 			}
239 			/* Emit the run-length encoded string. */
240 			while (runlen >= 97) {
241 				gdb_cur->gdb_putc('*');
242 				cksum += '*';
243 				gdb_cur->gdb_putc(97+29);
244 				cksum += 97+29;
245 				runlen -= 97;
246 				if (runlen > 0) {
247 					gdb_cur->gdb_putc(c);
248 					cksum += c;
249 					runlen--;
250 				}
251 			}
252 			if (runlen == 1) {
253 				gdb_cur->gdb_putc(c);
254 				cksum += c;
255 				runlen--;
256 			}
257 			if (runlen == 0)
258 				continue;
259 			/* Don't emit '$', '#', '+' or '-'. */
260 			if (runlen == 7) {
261 				gdb_cur->gdb_putc(c);
262 				cksum += c;
263 				runlen--;
264 			}
265 			if (runlen == 6 || runlen == 14 || runlen == 16) {
266 				gdb_cur->gdb_putc(c);
267 				cksum += c;
268 				runlen--;
269 			}
270 			gdb_cur->gdb_putc('*');
271 			cksum += '*';
272 			gdb_cur->gdb_putc(runlen+29);
273 			cksum += runlen+29;
274 		}
275 
276 		gdb_cur->gdb_putc('#');
277 		c = cksum >> 4;
278 		gdb_cur->gdb_putc(N2C(c));
279 		c = cksum & 0x0f;
280 		gdb_cur->gdb_putc(N2C(c));
281 
282 		c = gdb_getc();
283 	} while (c != '+');
284 
285 	return (0);
286 }
287 
288 int
289 gdb_tx_mem(const unsigned char *addr, size_t size)
290 {
291 	void *prev;
292 	jmp_buf jb;
293 	int ret;
294 
295 	prev = kdb_jmpbuf(jb);
296 	ret = setjmp(jb);
297 	if (ret == 0) {
298 		while (size-- > 0) {
299 			*gdb_txp++ = N2C(*addr >> 4);
300 			*gdb_txp++ = N2C(*addr & 0x0f);
301 			addr++;
302 		}
303 	}
304 	(void)kdb_jmpbuf(prev);
305 	return ((ret == 0) ? 1 : 0);
306 }
307 
308 void
309 gdb_tx_reg(int regnum)
310 {
311 	unsigned char *regp;
312 	size_t regsz;
313 
314 	regp = gdb_cpu_getreg(regnum, &regsz);
315 	if (regp == NULL) {
316 		/* Register unavailable. */
317 		while (regsz--) {
318 			gdb_tx_char('x');
319 			gdb_tx_char('x');
320 		}
321 	} else
322 		gdb_tx_mem(regp, regsz);
323 }
324 
325 /* Read binary data up until the end of the packet or until we have datalen decoded bytes */
326 int
327 gdb_rx_bindata(unsigned char *data, size_t datalen, size_t *amt)
328 {
329 	int c;
330 
331 	*amt = 0;
332 
333 	while (*amt < datalen) {
334 		c = gdb_rx_char();
335 		/* End of packet? */
336 		if (c == -1)
337 			break;
338 		/* Escaped character up next */
339 		if (c == '}') {
340 			/* Truncated packet? Bail out */
341 			if ((c = gdb_rx_char()) == -1)
342 				return (1);
343 			c ^= 0x20;
344 		}
345 		*(data++) = c & 0xff;
346 		(*amt)++;
347 	}
348 
349 	return (0);
350 }
351 
352 int
353 gdb_search_mem(const unsigned char *addr, size_t size, const unsigned char *pat, size_t patlen, const unsigned char **found)
354 {
355 	void *prev;
356 	jmp_buf jb;
357 	int ret;
358 
359 	prev = kdb_jmpbuf(jb);
360 	ret = setjmp(jb);
361 	if (ret == 0)
362 		*found = memmem(addr, size, pat, patlen);
363 
364 	(void)kdb_jmpbuf(prev);
365 	return ((ret == 0) ? 1 : 0);
366 }
367