/* * 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 processing for interfacing to gdb. */ static int handle_exception (struct pt_regs *regs) { int addr; int length; char *ptr; kgdb_data kd; int i; if (!initialized) { printf("kgdb: exception before kgdb is initialized! huh?\n"); return (0); } /* probably should check which exception occured as well */ if (longjmp_on_fault) { longjmp_on_fault = 0; kgdb_longjmp(error_jmp_buf, KGDBERR_MEMFAULT); panic("kgdb longjump failed!\n"); } if (kgdb_active) { printf("kgdb: unexpected exception from within kgdb\n"); return (0); } kgdb_active = 1; kgdb_interruptible(0); printf("kgdb: handle_exception; trap [0x%x]\n", kgdb_trap(regs)); if (kgdb_setjmp(error_jmp_buf) != 0) panic("kgdb: error or fault in entry init!\n"); kgdb_enter(regs, &kd); if (first_entry) { /* * the first time we enter kgdb, we save the processor * state so that we can return to the monitor if the * remote end quits gdb (or at least, tells us to quit * with the 'k' packet) */ entry_regs = *regs; first_entry = 0; } ptr = remcomOutBuffer; *ptr++ = 'T'; *ptr++ = hexchars[kd.sigval >> 4]; *ptr++ = hexchars[kd.sigval & 0xf]; for (i = 0; i < kd.nregs; i++) { kgdb_reg *rp = &kd.regs[i]; *ptr++ = hexchars[rp->num >> 4]; *ptr++ = hexchars[rp->num & 0xf]; *ptr++ = ':'; ptr = (char *)mem2hex((char *)&rp->val, ptr, 4); *ptr++ = ';'; } *ptr = 0; #ifdef KGDB_DEBUG if (kdebug) printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); #endif putpacket((unsigned char *)&remcomOutBuffer); while (1) { volatile int errnum; remcomOutBuffer[0] = 0; getpacket(remcomInBuffer); ptr = &remcomInBuffer[1]; #ifdef KGDB_DEBUG if (kdebug) printf("kgdb: remcomInBuffer: %s\n", remcomInBuffer); #endif errnum = kgdb_setjmp(error_jmp_buf); if (errnum == 0) switch (remcomInBuffer[0]) { case '?': /* report most recent signal */ remcomOutBuffer[0] = 'S'; remcomOutBuffer[1] = hexchars[kd.sigval >> 4]; remcomOutBuffer[2] = hexchars[kd.sigval & 0xf]; remcomOutBuffer[3] = 0; break; #ifdef KGDB_DEBUG case 'd': /* toggle debug flag */ kdebug ^= 1; break; #endif case 'g': /* return the value of the CPU registers. */ length = kgdb_getregs(regs, remcomRegBuffer, BUFMAX); mem2hex(remcomRegBuffer, remcomOutBuffer, length); break; case 'G': /* set the value of the CPU registers */ length = strlen(ptr); if ((length & 1) != 0) kgdb_error(KGDBERR_BADPARAMS); hex2mem(ptr, remcomRegBuffer, length/2); kgdb_putregs(regs, remcomRegBuffer, length/2); strcpy(remcomOutBuffer,"OK"); break; case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ /* Try to read %x,%x. */ if (hexToInt(&ptr, &addr) && *ptr++ == ',' && hexToInt(&ptr, &length)) { mem2hex((char *)addr, remcomOutBuffer, length); } else { kgdb_error(KGDBERR_BADPARAMS); } break; case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ /* Try to read '%x,%x:'. */ if (hexToInt(&ptr, &addr) && *ptr++ == ',' && hexToInt(&ptr, &length) && *ptr++ == ':') { hex2mem(ptr, (char *)addr, length); strcpy(remcomOutBuffer, "OK"); } else { kgdb_error(KGDBERR_BADPARAMS); } break; case 'k': /* kill the program, actually return to monitor */ kd.extype = KGDBEXIT_KILL; *regs = entry_regs; first_entry = 1; goto doexit; case 'C': /* CSS continue with signal SS */ *ptr = '\0'; /* ignore the signal number for now */ /* fall through */ case 'c': /* cAA..AA Continue; address AA..AA optional */ /* try to read optional parameter, pc unchanged if no parm */ kd.extype = KGDBEXIT_CONTINUE; if (hexToInt(&ptr, &addr)) { kd.exaddr = addr; kd.extype |= KGDBEXIT_WITHADDR; } goto doexit; case 'S': /* SSS single step with signal SS */ *ptr = '\0'; /* ignore the signal number for now */ /* fall through */ case 's': kd.extype = KGDBEXIT_SINGLE; if (hexToInt(&ptr, &addr)) { kd.exaddr = addr; kd.extype |= KGDBEXIT_WITHADDR; } doexit: /* Need to flush the instruction cache here, as we may have deposited a * breakpoint, and the icache probably has no way of knowing that a data ref to * some location may have changed something that is in the instruction cache. */ kgdb_flush_cache_all(); kgdb_exit(regs, &kd); kgdb_active = 0; kgdb_interruptible(1); return (1); case 'r': /* Reset (if user process..exit ???)*/ panic("kgdb reset."); break; case 'P': /* Pr=v set reg r to value v (r and v are hex) */ if (hexToInt(&ptr, &addr) && *ptr++ == '=' && ((length = strlen(ptr)) & 1) == 0) { hex2mem(ptr, remcomRegBuffer, length/2); kgdb_putreg(regs, addr, remcomRegBuffer, length/2); strcpy(remcomOutBuffer,"OK"); } else { kgdb_error(KGDBERR_BADPARAMS); } break; } /* switch */ if (errnum != 0) sprintf(remcomOutBuffer, "E%02d", errnum); #ifdef KGDB_DEBUG if (kdebug) printf("kgdb: remcomOutBuffer: %s\n", remcomOutBuffer); #endif /* reply to the request */ putpacket((unsigned char *)&remcomOutBuffer); } /* while(1) */ }