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