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 45 static char gdb_rxbuf[GDB_BUFSZ]; 46 char *gdb_rxp = NULL; 47 size_t gdb_rxsz = 0; 48 static char gdb_txbuf[GDB_BUFSZ]; 49 char *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 59 static int 60 gdb_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 85 int 86 gdb_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 132 int 133 gdb_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 145 int 146 gdb_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 179 int 180 gdb_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 != EOF) { 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 212 void 213 gdb_tx_begin(char tp) 214 { 215 216 gdb_txp = gdb_txbuf; 217 if (tp != '\0') 218 gdb_tx_char(tp); 219 } 220 221 int 222 gdb_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 293 int 294 gdb_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 313 void 314 gdb_tx_reg(int regnum) 315 { 316 unsigned char *regp; 317 size_t regsz; 318 319 regp = gdb_cpu_getreg(regnum, ®sz); 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 bool 331 gdb_txbuf_has_capacity(size_t req) 332 { 333 return (((char *)gdb_txbuf + sizeof(gdb_txbuf) - gdb_txp) >= req); 334 } 335 336 /* Read binary data up until the end of the packet or until we have datalen decoded bytes */ 337 int 338 gdb_rx_bindata(unsigned char *data, size_t datalen, size_t *amt) 339 { 340 int c; 341 342 *amt = 0; 343 344 while (*amt < datalen) { 345 c = gdb_rx_char(); 346 if (c == EOF) 347 break; 348 /* Escaped character up next */ 349 if (c == '}') { 350 /* Malformed packet. */ 351 if ((c = gdb_rx_char()) == EOF) 352 return (1); 353 c ^= 0x20; 354 } 355 *(data++) = c & 0xff; 356 (*amt)++; 357 } 358 359 return (0); 360 } 361 362 int 363 gdb_search_mem(const unsigned char *addr, size_t size, const unsigned char *pat, size_t patlen, const unsigned char **found) 364 { 365 void *prev; 366 jmp_buf jb; 367 int ret; 368 369 prev = kdb_jmpbuf(jb); 370 ret = setjmp(jb); 371 if (ret == 0) 372 *found = memmem(addr, size, pat, patlen); 373 374 (void)kdb_jmpbuf(prev); 375 return ((ret == 0) ? 1 : 0); 376 } 377