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