1139778Simp/*-
2131899Smarcel * Copyright (c) 2004 Marcel Moolenaar
3131899Smarcel * All rights reserved.
4131899Smarcel *
5131899Smarcel * Redistribution and use in source and binary forms, with or without
6131899Smarcel * modification, are permitted provided that the following conditions
7131899Smarcel * are met:
8131899Smarcel *
9131899Smarcel * 1. Redistributions of source code must retain the above copyright
10131899Smarcel *    notice, this list of conditions and the following disclaimer.
11131899Smarcel * 2. Redistributions in binary form must reproduce the above copyright
12131899Smarcel *    notice, this list of conditions and the following disclaimer in the
13131899Smarcel *    documentation and/or other materials provided with the distribution.
14131899Smarcel *
15131899Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16131899Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17131899Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18131899Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19131899Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20131899Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21131899Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22131899Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23131899Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24131899Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25131899Smarcel */
26131899Smarcel
27131899Smarcel#include <sys/cdefs.h>
28131899Smarcel__FBSDID("$FreeBSD$");
29131899Smarcel
30131899Smarcel#include <sys/param.h>
31131899Smarcel#include <sys/systm.h>
32131899Smarcel#include <sys/ctype.h>
33131899Smarcel#include <sys/kdb.h>
34218825Smdf#include <sys/ttydefaults.h>
35131899Smarcel
36131899Smarcel#include <machine/gdb_machdep.h>
37170473Smarcel#include <machine/kdb.h>
38131899Smarcel
39131899Smarcel#include <gdb/gdb.h>
40131899Smarcel#include <gdb/gdb_int.h>
41131899Smarcel
42131899Smarcelstatic char gdb_rxbuf[GDB_BUFSZ];
43131899Smarcelchar *gdb_rxp = NULL;
44131899Smarcelsize_t gdb_rxsz = 0;
45131899Smarcelstatic char gdb_txbuf[GDB_BUFSZ];
46131899Smarcelchar *gdb_txp = NULL;			/* Used in inline functions. */
47131899Smarcel
48131899Smarcel#define	C2N(c)	(((c) < 'A') ? (c) - '0' : \
49131899Smarcel	    10 + (((c) < 'a') ? (c) - 'A' : (c) - 'a'))
50131899Smarcel#define	N2C(n)	(((n) < 10) ? (n) + '0' : (n) + 'a' - 10)
51131899Smarcel
52131899Smarcel/*
53158948Sphk * Get a single character
54158948Sphk */
55158948Sphk
56158948Sphkstatic int
57158948Sphkgdb_getc(void)
58158948Sphk{
59158948Sphk	int c;
60158948Sphk
61158948Sphk	do
62158948Sphk		c = gdb_cur->gdb_getc();
63158948Sphk	while (c == -1);
64218825Smdf
65218825Smdf	if (c == CTRL('C')) {
66218825Smdf		printf("Received ^C; trying to switch back to ddb.\n");
67218825Smdf
68218825Smdf		if (kdb_dbbe_select("ddb") != 0)
69218825Smdf			printf("The ddb backend could not be selected.\n");
70218825Smdf		else {
71218825Smdf			printf("using longjmp, hope it works!\n");
72218825Smdf			kdb_reenter();
73218825Smdf		}
74218825Smdf	}
75158948Sphk	return (c);
76158948Sphk}
77158948Sphk
78158948Sphk/*
79131899Smarcel * Functions to receive and extract from a packet.
80131899Smarcel */
81131899Smarcel
82131899Smarcelint
83131899Smarcelgdb_rx_begin(void)
84131899Smarcel{
85131899Smarcel	int c, cksum;
86131899Smarcel
87131899Smarcel	gdb_rxp = NULL;
88131899Smarcel	do {
89131899Smarcel		/*
90131899Smarcel		 * Wait for the start character, ignore all others.
91131899Smarcel		 * XXX needs a timeout.
92131899Smarcel		 */
93158948Sphk		while ((c = gdb_getc()) != '$')
94131899Smarcel			;
95131899Smarcel
96131899Smarcel		/* Read until a # or end of buffer is found. */
97131899Smarcel		cksum = 0;
98131899Smarcel		gdb_rxsz = 0;
99131899Smarcel		while (gdb_rxsz < sizeof(gdb_rxbuf) - 1) {
100158948Sphk			c = gdb_getc();
101131899Smarcel			if (c == '#')
102131899Smarcel				break;
103131899Smarcel			gdb_rxbuf[gdb_rxsz++] = c;
104131899Smarcel			cksum += c;
105131899Smarcel		}
106131899Smarcel		gdb_rxbuf[gdb_rxsz] = 0;
107131899Smarcel		cksum &= 0xff;
108131899Smarcel
109131899Smarcel		/* Bail out on a buffer overflow. */
110131899Smarcel		if (c != '#') {
111131899Smarcel			gdb_cur->gdb_putc('-');
112131899Smarcel			return (ENOSPC);
113131899Smarcel		}
114131899Smarcel
115158948Sphk		c = gdb_getc();
116131899Smarcel		cksum -= (C2N(c) << 4) & 0xf0;
117158948Sphk		c = gdb_getc();
118131899Smarcel		cksum -= C2N(c) & 0x0f;
119131899Smarcel		gdb_cur->gdb_putc((cksum == 0) ? '+' : '-');
120131899Smarcel		if (cksum != 0)
121131899Smarcel			printf("GDB: packet `%s' has invalid checksum\n",
122131899Smarcel			    gdb_rxbuf);
123131899Smarcel	} while (cksum != 0);
124131899Smarcel
125131899Smarcel	gdb_rxp = gdb_rxbuf;
126131899Smarcel	return (0);
127131899Smarcel}
128131899Smarcel
129131899Smarcelint
130131899Smarcelgdb_rx_equal(const char *str)
131131899Smarcel{
132131899Smarcel	int len;
133131899Smarcel
134131899Smarcel	len = strlen(str);
135131899Smarcel	if (len > gdb_rxsz || strncmp(str, gdb_rxp, len) != 0)
136131899Smarcel		return (0);
137131899Smarcel	gdb_rxp += len;
138131899Smarcel	gdb_rxsz -= len;
139131899Smarcel	return (1);
140131899Smarcel}
141131899Smarcel
142131899Smarcelint
143131899Smarcelgdb_rx_mem(unsigned char *addr, size_t size)
144131899Smarcel{
145170473Smarcel	unsigned char *p;
146131899Smarcel	void *prev;
147131899Smarcel	jmp_buf jb;
148170473Smarcel	size_t cnt;
149131899Smarcel	int ret;
150131899Smarcel	unsigned char c;
151131899Smarcel
152131899Smarcel	if (size * 2 != gdb_rxsz)
153131899Smarcel		return (-1);
154131899Smarcel
155131899Smarcel	prev = kdb_jmpbuf(jb);
156131899Smarcel	ret = setjmp(jb);
157131899Smarcel	if (ret == 0) {
158170473Smarcel		p = addr;
159170473Smarcel		cnt = size;
160170473Smarcel		while (cnt-- > 0) {
161131899Smarcel			c = (C2N(gdb_rxp[0]) << 4) & 0xf0;
162131899Smarcel			c |= C2N(gdb_rxp[1]) & 0x0f;
163170473Smarcel			*p++ = c;
164131899Smarcel			gdb_rxsz -= 2;
165131899Smarcel			gdb_rxp += 2;
166131899Smarcel		}
167170473Smarcel		kdb_cpu_sync_icache(addr, size);
168131899Smarcel	}
169131899Smarcel	(void)kdb_jmpbuf(prev);
170131899Smarcel	return ((ret == 0) ? 1 : 0);
171131899Smarcel}
172131899Smarcel
173131899Smarcelint
174131899Smarcelgdb_rx_varhex(uintmax_t *vp)
175131899Smarcel{
176131899Smarcel	uintmax_t v;
177131899Smarcel	int c, neg;
178131899Smarcel
179131899Smarcel	c = gdb_rx_char();
180131899Smarcel	neg = (c == '-') ? 1 : 0;
181131899Smarcel	if (neg == 1)
182131899Smarcel		c = gdb_rx_char();
183131899Smarcel	if (!isxdigit(c)) {
184131899Smarcel		gdb_rxp -= ((c == -1) ? 0 : 1) + neg;
185131899Smarcel		gdb_rxsz += ((c == -1) ? 0 : 1) + neg;
186131899Smarcel		return (-1);
187131899Smarcel	}
188131899Smarcel	v = 0;
189131899Smarcel	do {
190131899Smarcel		v <<= 4;
191131899Smarcel		v += C2N(c);
192131899Smarcel		c = gdb_rx_char();
193131899Smarcel	} while (isxdigit(c));
194131899Smarcel	if (c != -1) {
195131899Smarcel		gdb_rxp--;
196131899Smarcel		gdb_rxsz++;
197131899Smarcel	}
198131899Smarcel	*vp = (neg) ? -v : v;
199131899Smarcel	return (0);
200131899Smarcel}
201131899Smarcel
202131899Smarcel/*
203131899Smarcel * Function to build and send a package.
204131899Smarcel */
205131899Smarcel
206131899Smarcelvoid
207131899Smarcelgdb_tx_begin(char tp)
208131899Smarcel{
209131899Smarcel
210131899Smarcel	gdb_txp = gdb_txbuf;
211131899Smarcel	if (tp != '\0')
212131899Smarcel		gdb_tx_char(tp);
213131899Smarcel}
214131899Smarcel
215131899Smarcelint
216131899Smarcelgdb_tx_end(void)
217131899Smarcel{
218131899Smarcel	const char *p;
219131899Smarcel	int runlen;
220131899Smarcel	unsigned char c, cksum;
221131899Smarcel
222131899Smarcel	do {
223131899Smarcel		gdb_cur->gdb_putc('$');
224131899Smarcel
225131899Smarcel		cksum = 0;
226131899Smarcel		p = gdb_txbuf;
227131899Smarcel		while (p < gdb_txp) {
228131899Smarcel			/* Send a character and start run-length encoding. */
229131899Smarcel			c = *p++;
230131899Smarcel			gdb_cur->gdb_putc(c);
231131899Smarcel			cksum += c;
232131899Smarcel			runlen = 0;
233131899Smarcel			/* Determine run-length and update checksum. */
234131899Smarcel			while (p < gdb_txp && *p == c) {
235131899Smarcel				runlen++;
236131899Smarcel				p++;
237131899Smarcel			}
238131899Smarcel			/* Emit the run-length encoded string. */
239131899Smarcel			while (runlen >= 97) {
240131899Smarcel				gdb_cur->gdb_putc('*');
241131899Smarcel				cksum += '*';
242131899Smarcel				gdb_cur->gdb_putc(97+29);
243131899Smarcel				cksum += 97+29;
244131899Smarcel				runlen -= 97;
245131899Smarcel				if (runlen > 0) {
246131899Smarcel					gdb_cur->gdb_putc(c);
247131899Smarcel					cksum += c;
248131899Smarcel					runlen--;
249131899Smarcel				}
250131899Smarcel			}
251131899Smarcel			if (runlen == 1) {
252131899Smarcel				gdb_cur->gdb_putc(c);
253131899Smarcel				cksum += c;
254131899Smarcel				runlen--;
255131899Smarcel			}
256131899Smarcel			if (runlen == 0)
257131899Smarcel				continue;
258131899Smarcel			/* Don't emit '$', '#', '+' or '-'. */
259131899Smarcel			if (runlen == 7) {
260131899Smarcel				gdb_cur->gdb_putc(c);
261131899Smarcel				cksum += c;
262131899Smarcel				runlen--;
263131899Smarcel			}
264131899Smarcel			if (runlen == 6 || runlen == 14 || runlen == 16) {
265131899Smarcel				gdb_cur->gdb_putc(c);
266131899Smarcel				cksum += c;
267131899Smarcel				runlen--;
268131899Smarcel			}
269131899Smarcel			gdb_cur->gdb_putc('*');
270131899Smarcel			cksum += '*';
271131899Smarcel			gdb_cur->gdb_putc(runlen+29);
272131899Smarcel			cksum += runlen+29;
273131899Smarcel		}
274131899Smarcel
275131899Smarcel		gdb_cur->gdb_putc('#');
276131899Smarcel		c = cksum >> 4;
277131899Smarcel		gdb_cur->gdb_putc(N2C(c));
278131899Smarcel		c = cksum & 0x0f;
279131899Smarcel		gdb_cur->gdb_putc(N2C(c));
280131899Smarcel
281158948Sphk		c = gdb_getc();
282131899Smarcel	} while (c != '+');
283131899Smarcel
284131899Smarcel	return (0);
285131899Smarcel}
286131899Smarcel
287131899Smarcelint
288131899Smarcelgdb_tx_mem(const unsigned char *addr, size_t size)
289131899Smarcel{
290131899Smarcel	void *prev;
291131899Smarcel	jmp_buf jb;
292131899Smarcel	int ret;
293131899Smarcel
294131899Smarcel	prev = kdb_jmpbuf(jb);
295131899Smarcel	ret = setjmp(jb);
296131899Smarcel	if (ret == 0) {
297131899Smarcel		while (size-- > 0) {
298131899Smarcel			*gdb_txp++ = N2C(*addr >> 4);
299131899Smarcel			*gdb_txp++ = N2C(*addr & 0x0f);
300131899Smarcel			addr++;
301131899Smarcel		}
302131899Smarcel	}
303131899Smarcel	(void)kdb_jmpbuf(prev);
304131899Smarcel	return ((ret == 0) ? 1 : 0);
305131899Smarcel}
306131899Smarcel
307131899Smarcelvoid
308131899Smarcelgdb_tx_reg(int regnum)
309131899Smarcel{
310131899Smarcel	unsigned char *regp;
311131899Smarcel	size_t regsz;
312131899Smarcel
313131899Smarcel	regp = gdb_cpu_getreg(regnum, &regsz);
314131899Smarcel	if (regp == NULL) {
315131899Smarcel		/* Register unavailable. */
316131899Smarcel		while (regsz--) {
317131899Smarcel			gdb_tx_char('x');
318131899Smarcel			gdb_tx_char('x');
319131899Smarcel		}
320131899Smarcel	} else
321131899Smarcel		gdb_tx_mem(regp, regsz);
322131899Smarcel}
323