Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/gdb/gdb_packet.c
34820 views
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
42
static char gdb_rxbuf[GDB_BUFSZ];
43
char *gdb_rxp = NULL;
44
size_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
*/
52
static 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");
66
char *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
76
static int
77
gdb_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
105
int
106
gdb_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
162
int
163
gdb_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
175
int
176
gdb_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
209
int
210
gdb_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
242
void
243
gdb_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
*/
255
static void
256
gdb_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
274
int
275
gdb_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
337
getack:
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
352
int
353
gdb_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
372
void
373
gdb_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
389
bool
390
gdb_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 */
396
int
397
gdb_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
421
int
422
gdb_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
437