X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fserver%2Fgdb_server.c;h=b15c29d38be3397c8f6df091d6c755f2019ce21d;hp=cd7e327f22a7054c4e3d6ebae8de88141f394e6d;hb=2a0317e6f40a4f2d5d20ccdaae82100f0ad4340a;hpb=02f3765351c9e25185b745b84f1a2604fb2149c7 diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index cd7e327f22..b15c29d38b 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -28,11 +28,11 @@ #include "server.h" #include "log.h" #include "binarybuffer.h" +#include "jtag.h" #include "breakpoints.h" #include "flash.h" #include "target_request.h" -#define __USE_GNU #include #include #include @@ -44,6 +44,10 @@ static unsigned short gdb_port; +static void gdb_log_callback(void *privData, const char *file, int line, + const char *function, const char *format, va_list args); + + enum gdb_detach_mode { GDB_DETACH_RESUME, @@ -52,8 +56,14 @@ enum gdb_detach_mode GDB_DETACH_NOTHING }; +/* target behaviour on gdb detach */ enum gdb_detach_mode detach_mode = GDB_DETACH_RESUME; +/* set if we are sending a memory map to gdb + * via qXfer:memory-map:read packet */ +int gdb_use_memory_map = 0; +int gdb_flash_program = 0; + int gdb_last_signal(target_t *target) { switch (target->debug_reason) @@ -77,7 +87,10 @@ int gdb_last_signal(target_t *target) int gdb_get_char(connection_t *connection, int* next_char) { gdb_connection_t *gdb_con = connection->priv; + +#ifdef _DEBUG_GDB_IO_ char *debug_buffer; +#endif if (gdb_con->buf_cnt-- > 0) { @@ -109,6 +122,8 @@ int gdb_get_char(connection_t *connection, int* next_char) break; case WSAECONNABORTED: return ERROR_SERVER_REMOTE_CLOSED; + case WSAECONNRESET: + return ERROR_SERVER_REMOTE_CLOSED; default: ERROR("read: %d", errno); exit(-1); @@ -130,11 +145,13 @@ int gdb_get_char(connection_t *connection, int* next_char) #endif } +#ifdef _DEBUG_GDB_IO_ debug_buffer = malloc(gdb_con->buf_cnt + 1); memcpy(debug_buffer, gdb_con->buffer, gdb_con->buf_cnt); debug_buffer[gdb_con->buf_cnt] = 0; DEBUG("received '%s'", debug_buffer); free(debug_buffer); +#endif gdb_con->buf_p = gdb_con->buffer; gdb_con->buf_cnt--; @@ -167,12 +184,14 @@ int gdb_putback_char(connection_t *connection, int last_char) return ERROR_OK; } -int gdb_put_packet(connection_t *connection, char *buffer, int len) +int gdb_put_packet_inner(connection_t *connection, char *buffer, int len) { int i; unsigned char my_checksum = 0; char checksum[3]; +#ifdef _DEBUG_GDB_IO_ char *debug_buffer; +#endif int reply; int retval; gdb_connection_t *gdb_con = connection->priv; @@ -182,12 +201,13 @@ int gdb_put_packet(connection_t *connection, char *buffer, int len) while (1) { +#ifdef _DEBUG_GDB_IO_ debug_buffer = malloc(len + 1); memcpy(debug_buffer, buffer, len); debug_buffer[len] = 0; DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum); free(debug_buffer); - +#endif write_socket(connection->fd, "$", 1); if (len > 0) write_socket(connection->fd, buffer, len); @@ -229,7 +249,16 @@ int gdb_put_packet(connection_t *connection, char *buffer, int len) return ERROR_OK; } -int gdb_get_packet(connection_t *connection, char *buffer, int *len) +int gdb_put_packet(connection_t *connection, char *buffer, int len) +{ + gdb_connection_t *gdb_connection = connection->priv; + gdb_connection->output_disable=1; + int retval=gdb_put_packet_inner(connection, buffer, len); + gdb_connection->output_disable=0; + return retval; +} + +int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len) { int character; int count = 0; @@ -245,7 +274,9 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len) if ((retval = gdb_get_char(connection, &character)) != ERROR_OK) return retval; +#ifdef _DEBUG_GDB_IO_ DEBUG("character: '%c'", character); +#endif switch (character) { @@ -268,13 +299,70 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len) } while (character != '$'); my_checksum = 0; - - do + + count=0; + gdb_connection_t *gdb_con = connection->priv; + for (;;) { + /* The common case is that we have an entire packet with no escape chars. + * We need to leave at least 2 bytes in the buffer to have + * gdb_get_char() update various bits and bobs correctly. + */ + if ((gdb_con->buf_cnt > 2) && ((gdb_con->buf_cnt+count) < *len)) + { + /* The compiler will struggle a bit with constant propagation and + * aliasing, so we help it by showing that these values do not + * change inside the loop + */ + int i; + char *buf = gdb_con->buf_p; + int run = gdb_con->buf_cnt - 2; + i = 0; + int done = 0; + while (i < run) + { + character = *buf++; + i++; + if (character == '#') + { + /* Danger! character can be '#' when esc is + * used so we need an explicit boolean for done here. + */ + done = 1; + break; + } + + if (character == '}') + { + /* data transmitted in binary mode (X packet) + * uses 0x7d as escape character */ + my_checksum += character & 0xff; + character = *buf++; + i++; + my_checksum += character & 0xff; + buffer[count++] = (character ^ 0x20) & 0xff; + } else + { + my_checksum += character & 0xff; + buffer[count++] = character & 0xff; + } + } + gdb_con->buf_p += i; + gdb_con->buf_cnt -= i; + if (done) + break; + } + if (count > *len) + { + ERROR("packet buffer too small"); + return ERROR_GDB_BUFFER_TOO_SMALL; + } + if ((retval = gdb_get_char(connection, &character)) != ERROR_OK) return retval; - if (character == '#') break; + if (character == '#') + break; if (character == '}') { @@ -292,12 +380,7 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len) buffer[count++] = character & 0xff; } - if (count > *len) - { - ERROR("packet buffer too small"); - return ERROR_GDB_BUFFER_TOO_SMALL; - } - } while (1); + } *len = count; @@ -322,12 +405,27 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len) return ERROR_OK; } -int gdb_output(struct command_context_s *context, char* line) +int gdb_get_packet(connection_t *connection, char *buffer, int *len) { - connection_t *connection = context->output_handler_priv; + gdb_connection_t *gdb_connection = connection->priv; + gdb_connection->output_disable=1; + int retval=gdb_get_packet_inner(connection, buffer, len); + gdb_connection->output_disable=0; + return retval; +} + +int gdb_output_con(connection_t *connection, char* line) +{ + gdb_connection_t *gdb_connection = connection->priv; char *hex_buffer; int i, bin_size; + /* check if output is enabled */ + if (gdb_connection->output_disable) + { + return ERROR_OK; + } + bin_size = strlen(line); hex_buffer = malloc(bin_size*2 + 4); @@ -345,6 +443,36 @@ int gdb_output(struct command_context_s *context, char* line) return ERROR_OK; } +int gdb_output(struct command_context_s *context, char* line) +{ + connection_t *connection = context->output_handler_priv; + return gdb_output_con(connection, line); +} + +int gdb_program_handler(struct target_s *target, enum target_event event, void *priv) +{ + FILE *script; + struct command_context_s *cmd_ctx = priv; + + if (target->gdb_program_script) + { + script = fopen(target->gdb_program_script, "r"); + if (!script) + { + ERROR("couldn't open script file %s", target->gdb_program_script); + return ERROR_OK; + } + + INFO("executing gdb_program script '%s'", target->gdb_program_script); + command_run_file(cmd_ctx, script, COMMAND_EXEC); + fclose(script); + + jtag_execute_queue(); + } + + return ERROR_OK; +} + int gdb_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv) { connection_t *connection = priv; @@ -357,6 +485,9 @@ int gdb_target_callback_event_handler(struct target_s *target, enum target_event case TARGET_EVENT_HALTED: if (gdb_connection->frontend_state == TARGET_RUNNING) { + // stop forwarding log packets! + log_setCallback(NULL, NULL); + if (gdb_connection->ctrl_c) { signal = 0x2; @@ -378,6 +509,9 @@ int gdb_target_callback_event_handler(struct target_s *target, enum target_event gdb_connection->frontend_state = TARGET_RUNNING; } break; + case TARGET_EVENT_GDB_PROGRAM: + gdb_program_handler(target, event, connection->cmd_ctx); + break; default: break; } @@ -400,7 +534,8 @@ int gdb_new_connection(connection_t *connection) gdb_connection->ctrl_c = 0; gdb_connection->frontend_state = TARGET_HALTED; gdb_connection->vflash_image = NULL; - + gdb_connection->output_disable = 0; + /* output goes through gdb connection */ command_set_output_handler(connection->cmd_ctx, gdb_output, connection); @@ -481,30 +616,33 @@ int gdb_last_signal_packet(connection_t *connection, target_t *target, char* pac return ERROR_OK; } -void gdb_str_to_target(target_t *target, char *str, char *tstr) +/* Convert register to string of bits. NB! The # of bits in the + * register might be non-divisible by 8(a byte), in which + * case an entire byte is shown. */ +void gdb_str_to_target(target_t *target, char *tstr, reg_t *reg) { - int str_len = strlen(str); + static const char *DIGITS = "0123456789abcdef"; int i; - if (str_len % 2) - { - ERROR("BUG: gdb value with uneven number of characters encountered: %s", str); - exit(-1); - } + u8 *buf; + int buf_len; + buf = reg->value; + buf_len = CEIL(reg->size, 8); if (target->endianness == TARGET_LITTLE_ENDIAN) { - for (i = 0; i < str_len; i+=2) + for (i = 0; i < buf_len; i++) { - tstr[str_len - i - 1] = str[i + 1]; - tstr[str_len - i - 2] = str[i]; + tstr[i*2] = DIGITS[(buf[i]>>4) & 0xf]; + tstr[i*2+1] = DIGITS[buf[i]&0xf]; } } else { - for (i = 0; i < str_len; i++) + for (i = 0; i < buf_len; i++) { - tstr[i] = str[i]; + tstr[(buf_len-1-i)*2] = DIGITS[(buf[i]>>4)&0xf]; + tstr[(buf_len-1-i)*2+1] = DIGITS[buf[i]&0xf]; } } } @@ -547,7 +685,9 @@ int gdb_get_registers_packet(connection_t *connection, target_t *target, char* p char *reg_packet_p; int i; +#ifdef _DEBUG_GDB_IO_ DEBUG("-"); +#endif if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK) { @@ -573,16 +713,18 @@ int gdb_get_registers_packet(connection_t *connection, target_t *target, char* p for (i = 0; i < reg_list_size; i++) { - char *hex_buf = buf_to_str(reg_list[i]->value, reg_list[i]->size, 16); - DEBUG("hex_buf: %s", hex_buf); - gdb_str_to_target(target, hex_buf, reg_packet_p); + gdb_str_to_target(target, reg_packet_p, reg_list[i]); reg_packet_p += CEIL(reg_list[i]->size, 8) * 2; - free(hex_buf); } - reg_packet_p = strndup(reg_packet, CEIL(reg_packet_size, 8) * 2); - DEBUG("reg_packet: %s", reg_packet_p); - free(reg_packet_p); +#ifdef _DEBUG_GDB_IO_ + { + char *reg_packet_p; + reg_packet_p = strndup(reg_packet, CEIL(reg_packet_size, 8) * 2); + DEBUG("reg_packet: %s", reg_packet_p); + free(reg_packet_p); + } +#endif gdb_put_packet(connection, reg_packet, CEIL(reg_packet_size, 8) * 2); free(reg_packet); @@ -600,7 +742,9 @@ int gdb_set_registers_packet(connection_t *connection, target_t *target, char *p int retval; char *packet_p; +#ifdef _DEBUG_GDB_IO_ DEBUG("-"); +#endif /* skip command character */ packet++; @@ -672,9 +816,10 @@ int gdb_get_register_packet(connection_t *connection, target_t *target, char *pa reg_t **reg_list; int reg_list_size; int retval; - char *hex_buf; +#ifdef _DEBUG_GDB_IO_ DEBUG("-"); +#endif if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK) { @@ -698,15 +843,12 @@ int gdb_get_register_packet(connection_t *connection, target_t *target, char *pa reg_packet = malloc(CEIL(reg_list[reg_num]->size, 8) * 2); - hex_buf = buf_to_str(reg_list[reg_num]->value, reg_list[reg_num]->size, 16); - - gdb_str_to_target(target, hex_buf, reg_packet); + gdb_str_to_target(target, reg_packet, reg_list[reg_num]); gdb_put_packet(connection, reg_packet, CEIL(reg_list[reg_num]->size, 8) * 2); free(reg_list); free(reg_packet); - free(hex_buf); return ERROR_OK; } @@ -783,7 +925,6 @@ int gdb_memory_packet_error(connection_t *connection, int retval) case ERROR_TARGET_NOT_HALTED: ERROR("gdb tried to read memory but we're not halted, dropping connection"); return ERROR_SERVER_REMOTE_CLOSED; - break; case ERROR_TARGET_DATA_ABORT: gdb_send_error(connection, EIO); break; @@ -851,6 +992,18 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac retval = target->type->read_memory(target, addr, 1, len, buffer); } +#if 0 + if (retval == ERROR_TARGET_DATA_ABORT) + { + /* TODO : Here we have to lie and send back all zero's lest stack traces won't work. + * At some point this might be fixed in GDB, in which case this code can be removed. + * http://sourceware.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gdb&pr=2395 + */ + memset(buffer, 0, len); + retval = ERROR_OK; + } +#endif + if (retval == ERROR_OK) { hex_buffer = malloc(len * 2 + 1); @@ -1172,10 +1325,94 @@ int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target, return ERROR_OK; } +/* print out a string and allocate more space as needed, mainly used for XML at this point */ +void xml_printf(int *retval, char **xml, int *pos, int *size, const char *fmt, ...) +{ + if (*retval != ERROR_OK) + { + return; + } + int first = 1; + + for (;;) + { + if ((*xml == NULL) || (!first)) + { + /* start by 0 to exercise all the code paths. + * Need minimum 2 bytes to fit 1 char and 0 terminator. */ + + *size = *size * 2 + 2; + char *t = *xml; + *xml = realloc(*xml, *size); + if (*xml == NULL) + { + if (t) + free(t); + *retval = ERROR_SERVER_REMOTE_CLOSED; + return; + } + } + + va_list ap; + int ret; + va_start(ap, fmt); + ret = vsnprintf(*xml + *pos, *size - *pos, fmt, ap); + va_end(ap); + if ((ret > 0) && ((ret + 1) < *size - *pos)) + { + *pos += ret; + return; + } + /* there was just enough or not enough space, allocate more. */ + first = 0; + } +} + +static int decode_xfer_read (char *buf, char **annex, int *ofs, unsigned int *len) +{ + char *separator; + + /* Extract and NUL-terminate the annex. */ + *annex = buf; + while (*buf && *buf != ':') + buf++; + if (*buf == '\0') + return -1; + *buf++ = 0; + + /* After the read marker and annex, qXfer looks like a + * traditional 'm' packet. */ + + *ofs = strtoul(buf, &separator, 16); + + if (*separator != ',') + return -1; + + *len = strtoul(separator+1, NULL, 16); + + return 0; +} + +int gdb_calc_blocksize(flash_bank_t *bank) +{ + int i; + int block_size = 0xffffffff; + + /* loop through all sectors and return smallest sector size */ + + for (i = 0; i < bank->num_sectors; i++) + { + if (bank->sectors[i].size < block_size) + block_size = bank->sectors[i].size; + } + + return block_size; +} + int gdb_query_packet(connection_t *connection, target_t *target, char *packet, int packet_size) { command_context_t *cmd_ctx = connection->cmd_ctx; - + if (strstr(packet, "qRcmd,")) { if (packet_size > 6) @@ -1190,19 +1427,22 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i cmd[i] = tmp; } cmd[(packet_size - 6)/2] = 0x0; + + /* We want to print all debug output to GDB connection */ + log_setCallback(gdb_log_callback, connection); + target_call_timer_callbacks(); command_run_line(cmd_ctx, cmd); free(cmd); } gdb_put_packet(connection, "OK", 2); return ERROR_OK; } - - if (strstr(packet, "qCRC:")) + else if (strstr(packet, "qCRC:")) { if (packet_size > 5) { int retval; - u8 gdb_reply[9]; + u8 gdb_reply[10]; char *separator; u32 checksum; u32 addr = 0; @@ -1219,13 +1459,13 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i return ERROR_SERVER_REMOTE_CLOSED; } - len = strtoul(separator+1, NULL, 16); + len = strtoul(separator + 1, NULL, 16); retval = target_checksum_memory(target, addr, len, &checksum); if (retval == ERROR_OK) { - snprintf(gdb_reply, 9, "C%2.2x", checksum); + snprintf(gdb_reply, 10, "C%8.8x", checksum); gdb_put_packet(connection, gdb_reply, 9); } else @@ -1237,6 +1477,137 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i return ERROR_OK; } } + else if (strstr(packet, "qSupported")) + { + /* we currently support packet size and qXfer:memory-map:read (if enabled) + * disable qXfer:features:read for the moment */ + int retval = ERROR_OK; + char *buffer = NULL; + int pos = 0; + int size = 0; + + xml_printf(&retval, &buffer, &pos, &size, + "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read-", + (GDB_BUFFER_SIZE - 1), gdb_use_memory_map == 1 ? '+' : '-'); + + if (retval != ERROR_OK) + { + gdb_send_error(connection, 01); + return ERROR_OK; + } + + gdb_put_packet(connection, buffer, strlen(buffer)); + free(buffer); + + return ERROR_OK; + } + else if (strstr(packet, "qXfer:memory-map:read::")) + { + /* We get away with only specifying flash here. Regions that are not + * specified are treated as if we provided no memory map(if not we + * could detect the holes and mark them as RAM). + * Normally we only execute this code once, but no big deal if we + * have to regenerate it a couple of times. */ + + flash_bank_t *p; + char *xml = NULL; + int size = 0; + int pos = 0; + int retval = ERROR_OK; + + int offset; + int length; + char *separator; + int blocksize; + + /* skip command character */ + packet += 23; + + offset = strtoul(packet, &separator, 16); + length = strtoul(separator + 1, &separator, 16); + + xml_printf(&retval, &xml, &pos, &size, "\n"); + + int i = 0; + for (;;) + { + p = get_flash_bank_by_num(i); + if (p == NULL) + break; + + /* if device has uneven sector sizes, eg. str7, lpc + * we pass the smallest sector size to gdb memory map */ + blocksize = gdb_calc_blocksize(p); + + xml_printf(&retval, &xml, &pos, &size, "\n" \ + "0x%x\n" \ + "\n", \ + p->base, p->size, blocksize); + i++; + } + + xml_printf(&retval, &xml, &pos, &size, "\n"); + + if (retval != ERROR_OK) + { + gdb_send_error(connection, retval); + return retval; + } + + if (offset + length > pos) + { + length = pos - offset; + } + + char *t = malloc(length + 1); + t[0] = 'l'; + memcpy(t + 1, xml + offset, length); + gdb_put_packet(connection, t, length + 1); + + free(t); + free(xml); + return ERROR_OK; + } + else if (strstr(packet, "qXfer:features:read:")) + { + char *xml = NULL; + int size = 0; + int pos = 0; + int retval = ERROR_OK; + + int offset; + int length; + char *annex; + + /* skip command character */ + packet += 20; + + if (decode_xfer_read( packet, &annex, &offset, &length ) < 0) + { + gdb_send_error(connection, 01); + return ERROR_OK; + } + + if (strcmp(annex, "target.xml") != 0) + { + gdb_send_error(connection, 01); + return ERROR_OK; + } + + xml_printf(&retval, &xml, &pos, &size, \ + "l\narm\n\n"); + + if (retval != ERROR_OK) + { + gdb_send_error(connection, retval); + return retval; + } + + gdb_put_packet(connection, xml, strlen(xml) + 1); + + free(xml); + return ERROR_OK; + } gdb_put_packet(connection, "", 0); return ERROR_OK; @@ -1248,10 +1619,19 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p gdb_service_t *gdb_service = connection->service->priv; int result; + /* if flash programming disabled - send a empty reply */ + + if (gdb_flash_program == 0) + { + gdb_put_packet(connection, "", 0); + return ERROR_OK; + } + if (strstr(packet, "vFlashErase:")) { unsigned long addr; unsigned long length; + char *parse = packet + 12; if (*parse == '\0') { @@ -1274,9 +1654,19 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p ERROR("incomplete vFlashErase packet received, dropping connection"); return ERROR_SERVER_REMOTE_CLOSED; } - + + /* disable gdb output while programming */ + gdb_connection->output_disable = 1; + + /* assume all sectors need erasing - stops any problems + * when flash_write is called multiple times */ + flash_set_dirty(); + + /* perform any target specific operations before the erase */ + target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_PROGRAM); + /* perform erase */ - if ((result = flash_erase(gdb_service->target, addr, length)) != ERROR_OK) + if ((result = flash_erase_address_range(gdb_service->target, addr, length)) != ERROR_OK) { /* GDB doesn't evaluate the actual error number returned, * treat a failed erase as an I/O error @@ -1286,7 +1676,10 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p } else gdb_put_packet(connection, "OK", 2); - + + /* reenable gdb output */ + gdb_connection->output_disable = 0; + return ERROR_OK; } @@ -1309,6 +1702,9 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p } length = packet_size - (parse - packet); + /* disable gdb output while programming */ + gdb_connection->output_disable = 1; + /* create a new image if there isn't already one */ if (gdb_connection->vflash_image == NULL) { @@ -1321,6 +1717,9 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p gdb_put_packet(connection, "OK", 2); + /* reenable gdb output */ + gdb_connection->output_disable = 0; + return ERROR_OK; } @@ -1329,8 +1728,12 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p u32 written; char *error_str; - /* process the flashing buffer */ - if ((result = flash_image_operation(gdb_service->target, gdb_connection->vflash_image, &written, &error_str, NULL, flash_image_op_write)) != ERROR_OK) + /* disable gdb output while programming */ + gdb_connection->output_disable = 1; + + /* process the flashing buffer. No need to erase as GDB + * always issues a vFlashErase first. */ + if ((result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, &error_str, NULL, 0)) != ERROR_OK) { if (result == ERROR_FLASH_DST_OUT_OF_BANK) gdb_put_packet(connection, "E.memtype", 9); @@ -1352,7 +1755,10 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p image_close(gdb_connection->vflash_image); free(gdb_connection->vflash_image); gdb_connection->vflash_image = NULL; - + + /* reenable gdb output */ + gdb_connection->output_disable = 0; + return ERROR_OK; } @@ -1385,6 +1791,22 @@ int gdb_detach(connection_t *connection, target_t *target) return ERROR_OK; } + + +static void gdb_log_callback(void *privData, const char *file, int line, + const char *function, const char *format, va_list args) +{ + connection_t *connection=(connection_t *)privData; + + char *t=allocPrintf(format, args); + if (t==NULL) + return; + + gdb_output_con(connection, t); + + free(t); +} + int gdb_input(connection_t *connection) { gdb_service_t *gdb_service = connection->service->priv; @@ -1393,6 +1815,7 @@ int gdb_input(connection_t *connection) int packet_size; int retval; gdb_connection_t *gdb_con = connection->priv; + static int extended_protocol = 0; /* drain input buffer */ do @@ -1458,6 +1881,9 @@ int gdb_input(connection_t *connection) break; case 'c': case 's': + /* We're running/stepping, in which case we can + * forward log output until the target is halted */ + log_setCallback(gdb_log_callback, connection); gdb_step_continue_packet(connection, target, packet, packet_size); break; case 'v': @@ -1465,14 +1891,26 @@ int gdb_input(connection_t *connection) break; case 'D': retval = gdb_detach(connection, target); + extended_protocol = 0; break; case 'X': if ((retval = gdb_write_memory_binary_packet(connection, target, packet, packet_size)) != ERROR_OK) return retval; break; case 'k': + if (extended_protocol != 0) + break; gdb_put_packet(connection, "OK", 2); return ERROR_SERVER_REMOTE_CLOSED; + case '!': + /* handle extended remote protocol */ + extended_protocol = 1; + gdb_put_packet(connection, "OK", 2); + break; + case 'R': + /* handle extended restart packet */ + target_process_reset(connection->cmd_ctx); + break; default: /* ignore unkown packets */ DEBUG("ignoring 0x%2.2x packet", packet[0]); @@ -1580,12 +2018,55 @@ int handle_gdb_detach_command(struct command_context_s *cmd_ctx, char *cmd, char return ERROR_OK; } +int handle_gdb_memory_map_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 1) + { + if (strcmp(args[0], "enable") == 0) + { + gdb_use_memory_map = 1; + return ERROR_OK; + } + else if (strcmp(args[0], "disable") == 0) + { + gdb_use_memory_map = 0; + return ERROR_OK; + } + } + + WARNING("invalid gdb_memory_map configuration directive: %s", args[0]); + return ERROR_OK; +} + +int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc == 1) + { + if (strcmp(args[0], "enable") == 0) + { + gdb_flash_program = 1; + return ERROR_OK; + } + else if (strcmp(args[0], "disable") == 0) + { + gdb_flash_program = 0; + return ERROR_OK; + } + } + + WARNING("invalid gdb_memory_map configuration directive: %s", args[0]); + return ERROR_OK; +} + int gdb_register_commands(command_context_t *command_context) { register_command(command_context, NULL, "gdb_port", handle_gdb_port_command, COMMAND_CONFIG, ""); register_command(command_context, NULL, "gdb_detach", handle_gdb_detach_command, COMMAND_CONFIG, ""); - + register_command(command_context, NULL, "gdb_memory_map", handle_gdb_memory_map_command, + COMMAND_CONFIG, ""); + register_command(command_context, NULL, "gdb_flash_program", handle_gdb_flash_program_command, + COMMAND_CONFIG, ""); return ERROR_OK; }