static void gdb_read_command() { u8 c; u8 chk_read, chk_calc; cmd_len = 0; memset(cmd_bfr, 0, sizeof cmd_bfr); c = gdb_read_byte(); if (c == '+') { // ignore ack return; } else if (c == 0x03) { CPU::Break(); gdb_signal(GDB_SIGTRAP); return; } else if (c != GDB_STUB_START) { DEBUG_LOG(GDB_STUB, "gdb: read invalid byte %02x", c); return; } while ((c = gdb_read_byte()) != GDB_STUB_END) { cmd_bfr[cmd_len++] = c; if (cmd_len == sizeof cmd_bfr) { ERROR_LOG(GDB_STUB, "gdb: cmd_bfr overflow"); gdb_nak(); return; } } chk_read = hex2char(gdb_read_byte()) << 4; chk_read |= hex2char(gdb_read_byte()); chk_calc = gdb_calc_chksum(); if (chk_calc != chk_read) { ERROR_LOG(GDB_STUB, "gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)", chk_calc, chk_read, cmd_bfr, cmd_len); cmd_len = 0; gdb_nak(); return; } DEBUG_LOG(GDB_STUB, "gdb: read command %c with a length of %d: %s", cmd_bfr[0], cmd_len, cmd_bfr); gdb_ack(); }
static size_t gdb_consume_packet(uint8_t *data, size_t len) { size_t done; debug(GDBSTUB_PKT, "consume (%D): [", len); #ifdef CONFIG_GDBSTUB_PKT_DBG debug_write(data, len); debug_write((uint8_t*)"]\n", 2); #endif while(len) { switch(*data) { case GDB_ACK_BYTE: if(!gdb_enabled()) { debug(GDBSTUB_PKT, "gdb connect\n"); gdb_enable(); } done = 1; break; case GDB_PKT_BYTE: done = gdb_parse_packet(data, len); if(!done) goto __end; break; case GDB_INT_BYTE: done = 1; debug(GDBSTUB_PKT, "interrupt sequence requested\n"); gdb_interrupt_sequence(); break; case GDB_NAK_BYTE: done = 1; gdb_ack(); break; default: done = 1; debug(GDBSTUB_PKT, "gdb_stub unsupported '\\x%x' (sz %D)\n", *data, len); gdb_unsupported(); break; } len -= done; data += done; } __end: return len; }
static void __gdb_cmd_resume(uint8_t stp) { dbg_resume(stp); gdb_ack(); gdb_set_lock(0); }
static int gdbserver_main(struct GDBState *s) { char buf[MAX_PACKET_LEN]; char outbuf[MAX_PACKET_LEN]; char *ptr; uint32_t addr, len; int32_t thread, type, ret; for (;;) { memset(buf, 0, sizeof(buf)); memset(outbuf, 0, sizeof(outbuf)); get_packet(s, &buf[0]); gdb_ack(s); printf("command (%lu): %s\n", strlen(buf), buf); ptr = &buf[3]; switch (buf[2]) { case '?': gdb_reply(s, "T05thread:00;"); break; case 'g': cpu_read_registers(s, outbuf); gdb_reply(s, outbuf); break; case 'H': type = *ptr++; thread = strtoull(ptr, (char **)&ptr, 16); if (thread <= 0) { gdb_reply(s, "OK"); break; } else if (thread >= 1) { gdb_reply(s, "E22"); break; } switch (type) { case 'c': case 'g': gdb_reply(s, "OK"); break; default: gdb_reply(s, "E22"); break; } break; case 'k': goto end_command; case 'm': /* +$m9200,4#98 */ addr = strtoull(ptr, (char **)&ptr, 16); if (*ptr == ',') ++ptr; len = strtoull(ptr, NULL, 16); cpu_read_memory(s, addr, len, outbuf); gdb_reply(s, outbuf); break; case 'p': outbuf[0] = '\0'; gdb_reply(s, outbuf); break; case 'q': if (!strncmp(ptr, "Supported", 9)) { snprintf(outbuf, sizeof(outbuf), "PacketSize=%x", MAX_PACKET_LEN); gdb_reply(s, outbuf); } else if (!strncmp(ptr, "Offset", 6)) { gdb_reply(s, "Text=00000000;Data=00000000;Bss=00000000"); } else if (*ptr == 'C') gdb_reply(s, "QC1"); else gdb_reply(s, ""); break; case 'v': if (!strncmp(ptr, "Cont", 4)) { ptr += 4; if (*ptr == '?') { gdb_reply(s, "vCont;c;C;s;S"); break; } //set_reg(s->env, REG_PC, get_reg(s->env, REG_PC)+4); set_reg(s->env, REG_PC, 0x8224); gdb_reply(s, "S05"); } else return 0; break; case 'z': case 'Z': type = strtoul(ptr, (char **)&ptr, 16); if (*ptr == ',') ptr++; addr = strtoull(ptr, (char **)&ptr, 16); if (*ptr == ',') ptr++; len = strtoull(ptr, (char **)&ptr, 16); if (buf[2] == 'Z') ret = gdb_breakpoint_insert(addr, len, type); else ret = gdb_breakpoint_remove(addr, len, type); if (ret >= 0) gdb_reply(s, "OK"); break; default: return 0; } } end_command: return 0; }
/* * command parsing an dispatching */ static int gdb_parse_command(unsigned int *regfile) { if(!gdb_verify_checksum()) { gdb_nak(); return INIT; } else { gdb_ack(); } switch(cmd[0]) { case 'H': { /* * Command H (actually Hct) is used to select * the current thread (-1 meaning all threads) * We just fake we recognize the the command * and send an 'OK' response. */ gdb_reply("OK"); } break; case 'q': { extern unsigned __data_start; extern unsigned __bss_start; /* * There are several q commands: * * qXXXX Request info about XXXX. * QXXXX=yyyy Set value of XXXX to yyyy. * qOffsets Get segment offsets * * Currently we only support the 'qOffsets' * form. * * *Note* that we actually have to lie, * At first thought looks like we should * return '_start', '__data_start' & * '__bss_start', however gdb gets * confused because the kernel link script * pre-links at 0x80000000. To keep gdb * gdb happy we just substract that amount. */ if(strcmp(cmd+1, "Offsets")== 0) { gdb_reply( "Text=%x;Data=%x;Bss=%x", 0, ((unsigned)(&__data_start))-0x80000000, ((unsigned)(&__bss_start))-0x80000000 ); } else { gdb_reply("ENS"); } } break; case '?': { /* * command '?' is used for retrieving the signal * that stopped the program. Fully implemeting * this command requires help from the debugger, * by now we just fake a SIGKILL */ gdb_reply("S09"); /* SIGKILL = 9 */ } break; case 'g': { /* * command 'g' is used for reading the register * file. Faked by now. * * For x86 the register order is: * * eax, ebx, ecx, edx, * esp, ebp, esi, edi, * eip, eflags, * cs, ss, ds, es * * Note that even thought the segment descriptors * are actually 16 bits wide, gdb requires them * as 32 bit integers. Note also that for some * reason (unknown to me) gdb wants the register * dump in *big endian* format. */ gdb_regreply(regfile, GDB_REGISTER_FILE_COUNT); } break; case 'm': { char *ptr; unsigned address; unsigned len; /* * The 'm' command has the form mAAA,LLL * where AAA is the address and LLL is the * number of bytes. */ ptr= cmd+1; address= 0; len= 0; while(ptr && *ptr && (*ptr!= ',')) { address<<= 4; address+= parse_nibble(*ptr); ptr+= 1; } if(*ptr== ',') { ptr+= 1; } while(ptr && *ptr) { len<<= 4; len+= parse_nibble(*ptr); ptr+= 1; } if(len> 128) { len= 128; } /* * We cannot directly access the requested memory * for gdb may be trying to access an stray pointer * We copy the memory to a safe buffer using * the bulletproof user_memcpy(). */ if(user_memcpy(safe_mem, (char*)address, len)< 0) { gdb_reply("E02"); } else { gdb_memreply(safe_mem, len); } } break; case 'k': { /* * Command 'k' actual semantics is 'kill the damn thing'. * However gdb sends that command when you disconnect * from a debug session. I guess that 'kill' for the * kernel would map to reboot... however that's a * a very mean thing to do, instead we just quit * the gdb state machine and fallback to the regular * kernel debugger command prompt. */ return QUIT; } break; default: { gdb_reply("E01"); } break; } return WAITACK; }