#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ctype.h>
#include <sys/kdb.h>
#include <sys/libkern.h>
#include <sys/ttydefaults.h>
#include <machine/gdb_machdep.h>
#include <machine/kdb.h>
#include <gdb/gdb.h>
#include <gdb/gdb_int.h>
static char gdb_rxbuf[GDB_BUFSZ];
char *gdb_rxp = NULL;
size_t gdb_rxsz = 0;
static union {
struct _midbuf {
char mb_pad1;
char mb_buf[GDB_BUFSZ];
char mb_pad2[4];
} __packed txu_midbuf;
char txu_fullbuf[GDB_BUFSZ + sizeof("$#..")];
} gdb_tx_u;
#define gdb_txbuf gdb_tx_u.txu_midbuf.mb_buf
#define gdb_tx_fullbuf gdb_tx_u.txu_fullbuf
_Static_assert(sizeof(gdb_tx_u.txu_midbuf) == sizeof(gdb_tx_u.txu_fullbuf) &&
offsetof(struct _midbuf, mb_buf) == 1,
"assertions necessary for correctness");
char *gdb_txp = NULL;
#define C2N(c) (((c) < 'A') ? (c) - '0' : \
10 + (((c) < 'a') ? (c) - 'A' : (c) - 'a'))
#define N2C(n) (((n) < 10) ? (n) + '0' : (n) + 'a' - 10)
static int
gdb_getc(void)
{
int c;
do
c = gdb_cur->gdb_getc();
while (c == -1);
if (c == CTRL('C')) {
printf("Received ^C; trying to switch back to ddb.\n");
if (gdb_cur->gdb_dbfeatures & GDB_DBGP_FEAT_WANTTERM)
gdb_cur->gdb_term();
if (kdb_dbbe_select("ddb") != 0)
printf("The ddb backend could not be selected.\n");
else {
printf("using longjmp, hope it works!\n");
kdb_reenter();
}
}
return (c);
}
int
gdb_rx_begin(void)
{
int c, cksum;
gdb_rxp = NULL;
do {
while ((c = gdb_getc()) != '$')
;
cksum = 0;
gdb_rxsz = 0;
while (gdb_rxsz < sizeof(gdb_rxbuf) - 1) {
c = gdb_getc();
if (c == '#')
break;
gdb_rxbuf[gdb_rxsz++] = c;
cksum += c;
}
gdb_rxbuf[gdb_rxsz] = 0;
cksum &= 0xff;
if (c != '#') {
gdb_nack();
return (ENOSPC);
}
if (!gdb_ackmode)
break;
c = gdb_getc();
cksum -= (C2N(c) << 4) & 0xf0;
c = gdb_getc();
cksum -= C2N(c) & 0x0f;
if (cksum == 0) {
gdb_ack();
} else {
gdb_nack();
printf("GDB: packet `%s' has invalid checksum\n",
gdb_rxbuf);
}
} while (cksum != 0);
gdb_rxp = gdb_rxbuf;
return (0);
}
int
gdb_rx_equal(const char *str)
{
int len;
len = strlen(str);
if (len > gdb_rxsz || strncmp(str, gdb_rxp, len) != 0)
return (0);
gdb_rxp += len;
gdb_rxsz -= len;
return (1);
}
int
gdb_rx_mem(unsigned char *addr, size_t size)
{
unsigned char *p;
void *prev;
void *wctx;
jmp_buf jb;
size_t cnt;
int ret;
unsigned char c;
if (size * 2 != gdb_rxsz)
return (-1);
wctx = gdb_begin_write();
prev = kdb_jmpbuf(jb);
ret = setjmp(jb);
if (ret == 0) {
p = addr;
cnt = size;
while (cnt-- > 0) {
c = (C2N(gdb_rxp[0]) << 4) & 0xf0;
c |= C2N(gdb_rxp[1]) & 0x0f;
*p++ = c;
gdb_rxsz -= 2;
gdb_rxp += 2;
}
kdb_cpu_sync_icache(addr, size);
}
(void)kdb_jmpbuf(prev);
gdb_end_write(wctx);
return ((ret == 0) ? 1 : 0);
}
int
gdb_rx_varhex(uintmax_t *vp)
{
uintmax_t v;
int c, neg;
c = gdb_rx_char();
neg = (c == '-') ? 1 : 0;
if (neg == 1)
c = gdb_rx_char();
if (!isxdigit(c)) {
gdb_rxp -= ((c == -1) ? 0 : 1) + neg;
gdb_rxsz += ((c == -1) ? 0 : 1) + neg;
return (-1);
}
v = 0;
do {
v <<= 4;
v += C2N(c);
c = gdb_rx_char();
} while (isxdigit(c));
if (c != EOF) {
gdb_rxp--;
gdb_rxsz++;
}
*vp = (neg) ? -v : v;
return (0);
}
void
gdb_tx_begin(char tp)
{
gdb_txp = gdb_txbuf;
if (tp != '\0')
gdb_tx_char(tp);
}
static void
gdb_tx_sendpacket(void)
{
size_t msglen, i;
unsigned char csum;
msglen = gdb_txp - gdb_txbuf;
gdb_tx_fullbuf[0] = '$';
csum = 0;
for (i = 0; i < msglen; i++)
csum += (unsigned char)gdb_txbuf[i];
snprintf(&gdb_tx_fullbuf[1 + msglen], 4, "#%02x", (unsigned)csum);
gdb_cur->gdb_sendpacket(gdb_tx_fullbuf, msglen + 4);
}
int
gdb_tx_end(void)
{
const char *p;
int runlen;
unsigned char c, cksum;
do {
if (gdb_cur->gdb_sendpacket != NULL) {
gdb_tx_sendpacket();
goto getack;
}
gdb_cur->gdb_putc('$');
cksum = 0;
p = gdb_txbuf;
while (p < gdb_txp) {
c = *p++;
gdb_cur->gdb_putc(c);
cksum += c;
runlen = 0;
while (p < gdb_txp && *p == c) {
runlen++;
p++;
}
while (runlen >= 97) {
gdb_cur->gdb_putc('*');
cksum += '*';
gdb_cur->gdb_putc(97+29);
cksum += 97+29;
runlen -= 97;
if (runlen > 0) {
gdb_cur->gdb_putc(c);
cksum += c;
runlen--;
}
}
while (runlen == 1 || runlen == 2 ||
runlen + 29 == '$' || runlen + 29 == '#' ||
runlen + 29 == '+' || runlen + 29 == '-') {
gdb_cur->gdb_putc(c);
cksum += c;
runlen--;
}
if (runlen == 0)
continue;
gdb_cur->gdb_putc('*');
cksum += '*';
gdb_cur->gdb_putc(runlen+29);
cksum += runlen+29;
}
gdb_cur->gdb_putc('#');
c = cksum >> 4;
gdb_cur->gdb_putc(N2C(c));
c = cksum & 0x0f;
gdb_cur->gdb_putc(N2C(c));
getack:
if (!gdb_ackmode)
break;
c = gdb_getc();
} while (c != '+');
return (0);
}
int
gdb_tx_mem(const unsigned char *addr, size_t size)
{
void *prev;
jmp_buf jb;
int ret;
prev = kdb_jmpbuf(jb);
ret = setjmp(jb);
if (ret == 0) {
while (size-- > 0) {
*gdb_txp++ = N2C(*addr >> 4);
*gdb_txp++ = N2C(*addr & 0x0f);
addr++;
}
}
(void)kdb_jmpbuf(prev);
return ((ret == 0) ? 1 : 0);
}
void
gdb_tx_reg(int regnum)
{
unsigned char *regp;
size_t regsz;
regp = gdb_cpu_getreg(regnum, ®sz);
if (regp == NULL) {
while (regsz--) {
gdb_tx_char('x');
gdb_tx_char('x');
}
} else
gdb_tx_mem(regp, regsz);
}
bool
gdb_txbuf_has_capacity(size_t req)
{
return (((char *)gdb_txbuf + sizeof(gdb_txbuf) - gdb_txp) >= req);
}
int
gdb_rx_bindata(unsigned char *data, size_t datalen, size_t *amt)
{
int c;
*amt = 0;
while (*amt < datalen) {
c = gdb_rx_char();
if (c == EOF)
break;
if (c == '}') {
if ((c = gdb_rx_char()) == EOF)
return (1);
c ^= 0x20;
}
*(data++) = c & 0xff;
(*amt)++;
}
return (0);
}
int
gdb_search_mem(const unsigned char *addr, size_t size, const unsigned char *pat, size_t patlen, const unsigned char **found)
{
void *prev;
jmp_buf jb;
int ret;
prev = kdb_jmpbuf(jb);
ret = setjmp(jb);
if (ret == 0)
*found = memmem(addr, size, pat, patlen);
(void)kdb_jmpbuf(prev);
return ((ret == 0) ? 1 : 0);
}