1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
30#include <sys/systm.h>
31#include <sys/ctype.h>
32#include <sys/kdb.h>
33#include <sys/libkern.h>
34#include <sys/ttydefaults.h>
35
36#include <machine/gdb_machdep.h>
37#include <machine/kdb.h>
38
39#include <gdb/gdb.h>
40#include <gdb/gdb_int.h>
41
42static char gdb_rxbuf[GDB_BUFSZ];
43char *gdb_rxp = NULL;
44size_t gdb_rxsz = 0;
45
46/*
47 * The goal here is to allow in-place framing without making the math around
48 * 'gdb_txbuf' more complicated.  A generous reading of union special rule for
49 * "common initial sequence" suggests this may be valid in standard C99 and
50 * later.
51 */
52static union {
53	struct _midbuf {
54		char mb_pad1;
55		char mb_buf[GDB_BUFSZ];
56		char mb_pad2[4];
57	} __packed txu_midbuf;
58	/* sizeof includes trailing nul byte and this is intentional. */
59	char txu_fullbuf[GDB_BUFSZ + sizeof("$#..")];
60} gdb_tx_u;
61#define	gdb_txbuf	gdb_tx_u.txu_midbuf.mb_buf
62#define	gdb_tx_fullbuf	gdb_tx_u.txu_fullbuf
63_Static_assert(sizeof(gdb_tx_u.txu_midbuf) == sizeof(gdb_tx_u.txu_fullbuf) &&
64    offsetof(struct _midbuf, mb_buf) == 1,
65    "assertions necessary for correctness");
66char *gdb_txp = NULL;			/* Used in inline functions. */
67
68#define	C2N(c)	(((c) < 'A') ? (c) - '0' : \
69	    10 + (((c) < 'a') ? (c) - 'A' : (c) - 'a'))
70#define	N2C(n)	(((n) < 10) ? (n) + '0' : (n) + 'a' - 10)
71
72/*
73 * Get a single character
74 */
75
76static int
77gdb_getc(void)
78{
79	int c;
80
81	do
82		c = gdb_cur->gdb_getc();
83	while (c == -1);
84
85	if (c == CTRL('C')) {
86		printf("Received ^C; trying to switch back to ddb.\n");
87
88		if (gdb_cur->gdb_dbfeatures & GDB_DBGP_FEAT_WANTTERM)
89			gdb_cur->gdb_term();
90
91		if (kdb_dbbe_select("ddb") != 0)
92			printf("The ddb backend could not be selected.\n");
93		else {
94			printf("using longjmp, hope it works!\n");
95			kdb_reenter();
96		}
97	}
98	return (c);
99}
100
101/*
102 * Functions to receive and extract from a packet.
103 */
104
105int
106gdb_rx_begin(void)
107{
108	int c, cksum;
109
110	gdb_rxp = NULL;
111	do {
112		/*
113		 * Wait for the start character, ignore all others.
114		 * XXX needs a timeout.
115		 */
116		while ((c = gdb_getc()) != '$')
117			;
118
119		/* Read until a # or end of buffer is found. */
120		cksum = 0;
121		gdb_rxsz = 0;
122		while (gdb_rxsz < sizeof(gdb_rxbuf) - 1) {
123			c = gdb_getc();
124			if (c == '#')
125				break;
126			gdb_rxbuf[gdb_rxsz++] = c;
127			cksum += c;
128		}
129		gdb_rxbuf[gdb_rxsz] = 0;
130		cksum &= 0xff;
131
132		/* Bail out on a buffer overflow. */
133		if (c != '#') {
134			gdb_nack();
135			return (ENOSPC);
136		}
137
138		/*
139		 * In Not-AckMode, we can assume reliable transport and neither
140		 * need to verify checksums nor send Ack/Nack.
141		 */
142		if (!gdb_ackmode)
143			break;
144
145		c = gdb_getc();
146		cksum -= (C2N(c) << 4) & 0xf0;
147		c = gdb_getc();
148		cksum -= C2N(c) & 0x0f;
149		if (cksum == 0) {
150			gdb_ack();
151		} else {
152			gdb_nack();
153			printf("GDB: packet `%s' has invalid checksum\n",
154			    gdb_rxbuf);
155		}
156	} while (cksum != 0);
157
158	gdb_rxp = gdb_rxbuf;
159	return (0);
160}
161
162int
163gdb_rx_equal(const char *str)
164{
165	int len;
166
167	len = strlen(str);
168	if (len > gdb_rxsz || strncmp(str, gdb_rxp, len) != 0)
169		return (0);
170	gdb_rxp += len;
171	gdb_rxsz -= len;
172	return (1);
173}
174
175int
176gdb_rx_mem(unsigned char *addr, size_t size)
177{
178	unsigned char *p;
179	void *prev;
180	void *wctx;
181	jmp_buf jb;
182	size_t cnt;
183	int ret;
184	unsigned char c;
185
186	if (size * 2 != gdb_rxsz)
187		return (-1);
188
189	wctx = gdb_begin_write();
190	prev = kdb_jmpbuf(jb);
191	ret = setjmp(jb);
192	if (ret == 0) {
193		p = addr;
194		cnt = size;
195		while (cnt-- > 0) {
196			c = (C2N(gdb_rxp[0]) << 4) & 0xf0;
197			c |= C2N(gdb_rxp[1]) & 0x0f;
198			*p++ = c;
199			gdb_rxsz -= 2;
200			gdb_rxp += 2;
201		}
202		kdb_cpu_sync_icache(addr, size);
203	}
204	(void)kdb_jmpbuf(prev);
205	gdb_end_write(wctx);
206	return ((ret == 0) ? 1 : 0);
207}
208
209int
210gdb_rx_varhex(uintmax_t *vp)
211{
212	uintmax_t v;
213	int c, neg;
214
215	c = gdb_rx_char();
216	neg = (c == '-') ? 1 : 0;
217	if (neg == 1)
218		c = gdb_rx_char();
219	if (!isxdigit(c)) {
220		gdb_rxp -= ((c == -1) ? 0 : 1) + neg;
221		gdb_rxsz += ((c == -1) ? 0 : 1) + neg;
222		return (-1);
223	}
224	v = 0;
225	do {
226		v <<= 4;
227		v += C2N(c);
228		c = gdb_rx_char();
229	} while (isxdigit(c));
230	if (c != EOF) {
231		gdb_rxp--;
232		gdb_rxsz++;
233	}
234	*vp = (neg) ? -v : v;
235	return (0);
236}
237
238/*
239 * Function to build and send a package.
240 */
241
242void
243gdb_tx_begin(char tp)
244{
245
246	gdb_txp = gdb_txbuf;
247	if (tp != '\0')
248		gdb_tx_char(tp);
249}
250
251/*
252 * Take raw packet buffer and perform typical GDB packet framing, but not run-
253 * length encoding, before forwarding to driver ::gdb_sendpacket() routine.
254 */
255static void
256gdb_tx_sendpacket(void)
257{
258	size_t msglen, i;
259	unsigned char csum;
260
261	msglen = gdb_txp - gdb_txbuf;
262
263	/* Add GDB packet framing */
264	gdb_tx_fullbuf[0] = '$';
265
266	csum = 0;
267	for (i = 0; i < msglen; i++)
268		csum += (unsigned char)gdb_txbuf[i];
269	snprintf(&gdb_tx_fullbuf[1 + msglen], 4, "#%02x", (unsigned)csum);
270
271	gdb_cur->gdb_sendpacket(gdb_tx_fullbuf, msglen + 4);
272}
273
274int
275gdb_tx_end(void)
276{
277	const char *p;
278	int runlen;
279	unsigned char c, cksum;
280
281	do {
282		if (gdb_cur->gdb_sendpacket != NULL) {
283			gdb_tx_sendpacket();
284			goto getack;
285		}
286
287		gdb_cur->gdb_putc('$');
288
289		cksum = 0;
290		p = gdb_txbuf;
291		while (p < gdb_txp) {
292			/* Send a character and start run-length encoding. */
293			c = *p++;
294			gdb_cur->gdb_putc(c);
295			cksum += c;
296			runlen = 0;
297			/* Determine run-length and update checksum. */
298			while (p < gdb_txp && *p == c) {
299				runlen++;
300				p++;
301			}
302			/* Emit the run-length encoded string. */
303			while (runlen >= 97) {
304				gdb_cur->gdb_putc('*');
305				cksum += '*';
306				gdb_cur->gdb_putc(97+29);
307				cksum += 97+29;
308				runlen -= 97;
309				if (runlen > 0) {
310					gdb_cur->gdb_putc(c);
311					cksum += c;
312					runlen--;
313				}
314			}
315			/* Don't emit '$', '#', '+', '-' or a run length below 3. */
316			while (runlen == 1 || runlen == 2 ||
317			    runlen + 29 == '$' || runlen + 29 == '#' ||
318			    runlen + 29 == '+' || runlen + 29 == '-') {
319				gdb_cur->gdb_putc(c);
320				cksum += c;
321				runlen--;
322			}
323			if (runlen == 0)
324				continue;
325			gdb_cur->gdb_putc('*');
326			cksum += '*';
327			gdb_cur->gdb_putc(runlen+29);
328			cksum += runlen+29;
329		}
330
331		gdb_cur->gdb_putc('#');
332		c = cksum >> 4;
333		gdb_cur->gdb_putc(N2C(c));
334		c = cksum & 0x0f;
335		gdb_cur->gdb_putc(N2C(c));
336
337getack:
338		/*
339		 * In NoAckMode, it is assumed that the underlying transport is
340		 * reliable and thus neither conservant sends acknowledgements;
341		 * there is nothing to wait for here.
342		 */
343		if (!gdb_ackmode)
344			break;
345
346		c = gdb_getc();
347	} while (c != '+');
348
349	return (0);
350}
351
352int
353gdb_tx_mem(const unsigned char *addr, size_t size)
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		while (size-- > 0) {
363			*gdb_txp++ = N2C(*addr >> 4);
364			*gdb_txp++ = N2C(*addr & 0x0f);
365			addr++;
366		}
367	}
368	(void)kdb_jmpbuf(prev);
369	return ((ret == 0) ? 1 : 0);
370}
371
372void
373gdb_tx_reg(int regnum)
374{
375	unsigned char *regp;
376	size_t regsz;
377
378	regp = gdb_cpu_getreg(regnum, &regsz);
379	if (regp == NULL) {
380		/* Register unavailable. */
381		while (regsz--) {
382			gdb_tx_char('x');
383			gdb_tx_char('x');
384		}
385	} else
386		gdb_tx_mem(regp, regsz);
387}
388
389bool
390gdb_txbuf_has_capacity(size_t req)
391{
392	return (((char *)gdb_txbuf + sizeof(gdb_txbuf) - gdb_txp) >= req);
393}
394
395/* Read binary data up until the end of the packet or until we have datalen decoded bytes */
396int
397gdb_rx_bindata(unsigned char *data, size_t datalen, size_t *amt)
398{
399	int c;
400
401	*amt = 0;
402
403	while (*amt < datalen) {
404		c = gdb_rx_char();
405		if (c == EOF)
406			break;
407		/* Escaped character up next */
408		if (c == '}') {
409			/* Malformed packet. */
410			if ((c = gdb_rx_char()) == EOF)
411				return (1);
412			c ^= 0x20;
413		}
414		*(data++) = c & 0xff;
415		(*amt)++;
416	}
417
418	return (0);
419}
420
421int
422gdb_search_mem(const unsigned char *addr, size_t size, const unsigned char *pat, size_t patlen, const unsigned char **found)
423{
424	void *prev;
425	jmp_buf jb;
426	int ret;
427
428	prev = kdb_jmpbuf(jb);
429	ret = setjmp(jb);
430	if (ret == 0)
431		*found = memmem(addr, size, pat, patlen);
432
433	(void)kdb_jmpbuf(prev);
434	return ((ret == 0) ? 1 : 0);
435}
436