/* * This function does all command processing for interfacing to * a remote gdb. Note that the error codes are ignored by gdb * at present, but might eventually become meaningful. (XXX) * It might makes sense to use POSIX errno values, because * that is what the gdb/remote.c functions want to return. */ int kgdb_trap(int type, db_regs_t *regs) { label_t jmpbuf; vaddr_t addr; size_t len; u_char *p; kgdb_entry_notice(type, regs); if (kgdb_dev == NODEV || kgdb_getc == NULL) { /* not debugging */ return (0); } db_clear_single_step(regs); if (db_trap_callback) db_trap_callback(1); /* Detect and recover from unexpected traps. */ if (kgdb_recover != 0) { printf("kgdb: caught trap 0x%x at %p\n", type, (void*)PC_REGS(regs)); kgdb_send("E0E"); /* 14==EFAULT */ longjmp(kgdb_recover); } /* * The first entry to this function is normally through * a breakpoint trap in kgdb_connect(), in which case we * must advance past the breakpoint because gdb will not. * * Machines vary as to where they leave the PC after a * breakpoint trap. Those that leave the PC set to the * address of the trap instruction (i.e. pc532) will not * define FIXUP_PC_AFTER_BREAK(), and therefore will just * advance the PC. On machines that leave the PC set to * the instruction after the trap, FIXUP_PC_AFTER_BREAK * will be defined to back-up the PC, so that after the * "first-time" part of the if statement below has run, * the PC will be the same as it was on entry. * * On the first entry here, we expect that gdb is not yet * listening to us, so just enter the interaction loop. * After the debugger is "active" (connected) it will be * waiting for a "signaled" message from us. */ if (kgdb_active == 0) { if (!IS_BREAKPOINT_TRAP(type, 0)) { /* No debugger active -- let trap handle this. */ if (db_trap_callback) db_trap_callback(0); return (0); } /* Make the PC point at the breakpoint... */ #ifdef FIXUP_PC_AFTER_BREAK FIXUP_PC_AFTER_BREAK(regs); #endif /* ... and then advance past it. */ #ifdef PC_ADVANCE PC_ADVANCE(regs); #else PC_REGS(regs) += BKPT_SIZE; #endif kgdb_active = 1; } else { /* Tell remote host that an exception has occurred. */ snprintf(buffer, sizeof(buffer), "S%02x", kgdb_signal(type)); kgdb_send(buffer); } /* Stick frame regs into our reg cache. */ kgdb_getregs(regs, gdb_regs); /* * Interact with gdb until it lets us go. * If we cause a trap, resume here. */ (void)setjmp((kgdb_recover = &jmpbuf)); for (;;) { kgdb_recv(buffer, sizeof(buffer)); switch (buffer[0]) { default: /* Unknown command. */ kgdb_send(""); continue; case KGDB_SIGNAL: /* * if this command came from a running gdb, * answer it -- the other guy has no way of * knowing if we're in or out of this loop * when he issues a "remote-signal". */ snprintf(buffer, sizeof(buffer), "S%02x", kgdb_signal(type)); kgdb_send(buffer); continue; case KGDB_REG_R: mem2hex(buffer, gdb_regs, sizeof(gdb_regs)); kgdb_send(buffer); continue; case KGDB_REG_W: p = hex2mem(gdb_regs, buffer + 1, sizeof(gdb_regs)); if (p == NULL || *p != '\0') kgdb_send("E01"); else { kgdb_setregs(regs, gdb_regs); kgdb_send("OK"); } continue; case KGDB_MEM_R: p = buffer + 1; addr = hex2i(&p); if (*p++ != ',') { kgdb_send("E02"); continue; } len = hex2i(&p); if (*p != '\0') { kgdb_send("E03"); continue; } if (len > sizeof(buffer) / 2) { kgdb_send("E04"); continue; } if (kgdb_acc(addr, len) == 0) { kgdb_send("E05"); continue; } char *ptr = (char *)buffer + sizeof(buffer) / 2; db_read_bytes(addr, len, ptr); mem2hex(buffer, ptr, len); kgdb_send(buffer); continue; case KGDB_MEM_W: p = buffer + 1; addr = hex2i(&p); if (*p++ != ',') { kgdb_send("E06"); continue; } len = hex2i(&p); if (*p++ != ':') { kgdb_send("E07"); continue; } if (len > (sizeof(buffer) - (p - buffer))) { kgdb_send("E08"); continue; } p = hex2mem(buffer, p, sizeof(buffer)); if (p == NULL) { kgdb_send("E09"); continue; } if (kgdb_acc(addr, len) == 0) { kgdb_send("E0A"); continue; } db_write_bytes(addr, len, (char *)buffer); kgdb_send("OK"); continue; case KGDB_DETACH: case KGDB_KILL: kgdb_active = 0; printf("kgdb detached\n"); db_clear_single_step(regs); kgdb_send("OK"); goto out; case KGDB_CONT: if (buffer[1]) { p = buffer + 1; addr = hex2i(&p); if (*p) { kgdb_send("E0B"); continue; } PC_REGS(regs) = addr; DPRINTF(("kgdb: continuing at %08lx\n", addr)); } else { DPRINTF(( "kgdb: continuing at old address %08lx\n", (vaddr_t)PC_REGS(regs))); } db_clear_single_step(regs); goto out; case KGDB_STEP: if (buffer[1]) { p = buffer + 1; addr = hex2i(&p); if (*p) { kgdb_send("E0B"); continue; } PC_REGS(regs) = addr; } db_set_single_step(regs); goto out; } } out: if (db_trap_callback) db_trap_callback(0); kgdb_recover = 0; return (1); }
/* * This function does all command procesing for interfacing to * a remote gdb. */ int kgdb_trap(int type, struct frame *frame) { u_long len; u_char *addr; u_char *cp; u_char out, in; int outlen; int inlen; u_long gdb_regs[NUM_REGS]; if ((int)kgdb_dev < 0) { /* not debugging */ return (0); } if (kgdb_active == 0) { if (type != T_TRAP15) { /* No debugger active -- let trap handle this. */ return (0); } kgdb_getc = 0; for (inlen = 0; constab[inlen].cn_probe; inlen++) if (major(constab[inlen].cn_dev) == major(kgdb_dev)) { kgdb_getc = constab[inlen].cn_getc; kgdb_putc = constab[inlen].cn_putc; break; } if (kgdb_getc == 0 || kgdb_putc == 0) return (0); /* * If the packet that woke us up isn't an exec packet, * ignore it since there is no active debugger. Also, * we check that it's not an ack to be sure that the * remote side doesn't send back a response after the * local gdb has exited. Otherwise, the local host * could trap into gdb if it's running a gdb kernel too. */ in = GETC; /* * If we came in asynchronously through the serial line, * the framing character is eaten by the receive interrupt, * but if we come in through a synchronous trap (i.e., via * kgdb_connect()), we will see the extra character. */ if (in == FRAME_START) in = GETC; /* * Check that this is a debugger exec message. If so, * slurp up the entire message then ack it, and fall * through to the recv loop. */ if (KGDB_CMD(in) != KGDB_EXEC || (in & KGDB_ACK) != 0) return (0); while (GETC != FRAME_END) ; /* * Do the printf *before* we ack the message. This way * we won't drop any inbound characters while we're * doing the polling printf. */ printf("kgdb started from device %x\n", kgdb_dev); kgdb_send(in | KGDB_ACK, (u_char *)0, 0); kgdb_active = 1; } /* * Stick frame regs into our reg cache then tell remote host * that an exception has occurred. */ regs_to_gdb(frame, gdb_regs); if (type != T_TRAP15) { /* * Only send an asynchronous SIGNAL message when we hit * a breakpoint. Otherwise, we will drop the incoming * packet while we output this one (and on entry the other * side isn't interested in the SIGNAL type -- if it is, * it will have used a signal packet.) */ outbuffer[0] = computeSignal(type); kgdb_send(KGDB_SIGNAL, outbuffer, 1); } while (1) { in = kgdb_recv(inbuffer, &inlen); if (in == 0 || (in & KGDB_ACK)) /* Ignore inbound acks and error conditions. */ continue; out = in | KGDB_ACK; switch (KGDB_CMD(in)) { case KGDB_SIGNAL: /* * if this command came from a running gdb, * answer it -- the other guy has no way of * knowing if we're in or out of this loop * when he issues a "remote-signal". (Note * that without the length check, we could * loop here forever if the ourput line is * looped back or the remote host is echoing.) */ if (inlen == 0) { outbuffer[0] = computeSignal(type); kgdb_send(KGDB_SIGNAL, outbuffer, 1); } continue; case KGDB_REG_R: case KGDB_REG_R | KGDB_DELTA: cp = outbuffer; outlen = 0; for (len = inbuffer[0]; len < NUM_REGS; ++len) { if (reg_cache[len] != gdb_regs[len] || (in & KGDB_DELTA) == 0) { if (outlen + 5 > SL_MAXDATA) { out |= KGDB_MORE; break; } cp[outlen] = len; kgdb_copy((u_char *)&gdb_regs[len], &cp[outlen + 1], 4); reg_cache[len] = gdb_regs[len]; outlen += 5; } } break; case KGDB_REG_W: case KGDB_REG_W | KGDB_DELTA: cp = inbuffer; for (len = 0; len < inlen; len += 5) { int j = cp[len]; kgdb_copy(&cp[len + 1], (u_char *)&gdb_regs[j], 4); reg_cache[j] = gdb_regs[j]; } gdb_to_regs(frame, gdb_regs); outlen = 0; break; case KGDB_MEM_R: len = inbuffer[0]; kgdb_copy(&inbuffer[1], (u_char *)&addr, 4); if (len > SL_MAXDATA) { outlen = 1; outbuffer[0] = E2BIG; } else if (!kgdb_acc(addr, len, B_READ)) { outlen = 1; outbuffer[0] = EFAULT; } else { outlen = len + 1; outbuffer[0] = 0; kgdb_copy(addr, &outbuffer[1], len); } break; case KGDB_MEM_W: len = inlen - 4; kgdb_copy(inbuffer, (u_char *)&addr, 4); outlen = 1; if (!kgdb_acc(addr, len, B_READ)) outbuffer[0] = EFAULT; else { outbuffer[0] = 0; if (!kgdb_acc(addr, len, B_WRITE)) chgkprot(addr, len, B_WRITE); kgdb_copy(&inbuffer[4], addr, len); ICIA(); } break; case KGDB_KILL: kgdb_active = 0; printf("kgdb detached\n"); /* fall through */ case KGDB_CONT: kgdb_send(out, 0, 0); frame->f_sr &=~ PSL_T; return (1); case KGDB_STEP: kgdb_send(out, 0, 0); frame->f_sr |= PSL_T; return (1); case KGDB_EXEC: default: /* Unknown command. Ack with a null message. */ outlen = 0; break; } /* Send the reply */ kgdb_send(out, outbuffer, outlen); } }