X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Fserver%2Fgdb_server.c;h=08daa68a945acba10f88272b76fd2a553566a09e;hb=88907cc7f941ce85f0dc35ed3dbc4d2dbc87cef7;hp=cb14cc3f8f3fe62970dad832c0387fc86a002ace;hpb=b4c4b5f71ef069a641adad541b42ef79ca43ac60;p=openocd.git diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index cb14cc3f8f..08daa68a94 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -2,7 +2,7 @@ * Copyright (C) 2005 by Dominic Rath * * Dominic.Rath@gmx.de * * * - * Copyright (C) 2007,2008 Øyvind Harboe * + * Copyright (C) 2007-2009 Øyvind Harboe * * oyvind.harboe@zylin.com * * * * Copyright (C) 2008 by Spencer Oliver * @@ -27,14 +27,33 @@ #include "config.h" #endif -#include "breakpoints.h" -#include "target_request.h" -#include "register.h" +#include +#include +#include #include "server.h" -#include "flash.h" +#include #include "gdb_server.h" -#include "image.h" -#include "jtag.h" +#include +#include + + +/* private connection data for GDB */ +struct gdb_connection +{ + char buffer[GDB_BUFFER_SIZE]; + char *buf_p; + int buf_cnt; + int ctrl_c; + enum target_state frontend_state; + struct image *vflash_image; + int closed; + int busy; + int noack_mode; + bool sync; /* set flag to true if you want the next stepi to return immediately. + allowing GDB to pick up a fresh set of register values from the target + without modifying the target state. */ + +}; #if 0 @@ -48,12 +67,13 @@ static enum breakpoint_type gdb_breakpoint_override_type; extern int gdb_error(struct connection *connection, int retval); static unsigned short gdb_port = 3333; +static unsigned short gdb_port_next = 0; static const char *DIGITS = "0123456789abcdef"; static void gdb_log_callback(void *priv, const char *file, unsigned line, const char *function, const char *string); -/* number of gdb connections, mainly to supress gdb related debugging spam +/* number of gdb connections, mainly to suppress gdb related debugging spam * in helper/log.c when no gdb connections are actually active */ int gdb_actual_connections; @@ -129,30 +149,11 @@ int check_pending(struct connection *connection, int timeout_s, int *got_data) return ERROR_OK; } -int gdb_get_char(struct connection *connection, int* next_char) +static int gdb_get_char_inner(struct connection *connection, int* next_char) { struct gdb_connection *gdb_con = connection->priv; int retval = ERROR_OK; -#ifdef _DEBUG_GDB_IO_ - char *debug_buffer; -#endif - - if (gdb_con->buf_cnt-- > 0) - { - *next_char = *(gdb_con->buf_p++); - if (gdb_con->buf_cnt > 0) - connection->input_pending = 1; - else - connection->input_pending = 0; - -#ifdef _DEBUG_GDB_IO_ - LOG_DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char); -#endif - - return ERROR_OK; - } - for (;;) { if (connection->service->type == CONNECTION_PIPE) @@ -237,6 +238,50 @@ int gdb_get_char(struct connection *connection, int* next_char) return retval; } +/** + * The cool thing about this fn is that it allows buf_p and buf_cnt to be + * held in registers in the inner loop. + * + * For small caches and embedded systems this is important! + */ +static inline int gdb_get_char_fast(struct connection *connection, int* next_char, char **buf_p, int *buf_cnt) +{ + int retval = ERROR_OK; + + if ((*buf_cnt)-- > 0) + { + *next_char = **buf_p; + (*buf_p)++; + if (*buf_cnt > 0) + connection->input_pending = 1; + else + connection->input_pending = 0; + +#ifdef _DEBUG_GDB_IO_ + LOG_DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char); +#endif + + return ERROR_OK; + } + + struct gdb_connection *gdb_con = connection->priv; + gdb_con->buf_p = *buf_p; + gdb_con->buf_cnt = *buf_cnt; + retval = gdb_get_char_inner(connection, next_char); + *buf_p = gdb_con->buf_p; + *buf_cnt = gdb_con->buf_cnt; + + return retval; +} + + +int gdb_get_char(struct connection *connection, int* next_char) +{ + struct gdb_connection *gdb_con = connection->priv; + return gdb_get_char_fast(connection, next_char, &gdb_con->buf_p, &gdb_con->buf_cnt); +} + + int gdb_putback_char(struct connection *connection, int last_char) { struct gdb_connection *gdb_con = connection->priv; @@ -441,27 +486,33 @@ static __inline__ int fetch_packet(struct connection *connection, int *checksum_ unsigned char my_checksum = 0; char checksum[3]; int character; - int retval; + int retval = ERROR_OK; struct gdb_connection *gdb_con = connection->priv; my_checksum = 0; int count = 0; count = 0; + + /* move this over into local variables to use registers and give the + * more freedom to optimize */ + char *buf_p = gdb_con->buf_p; + int buf_cnt = gdb_con->buf_cnt; + 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)) + if ((buf_cnt > 2) && ((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; + char *buf = buf_p; + int run = buf_cnt - 2; i = 0; int done = 0; while (i < run) @@ -493,19 +544,21 @@ static __inline__ int fetch_packet(struct connection *connection, int *checksum_ buffer[count++] = character & 0xff; } } - gdb_con->buf_p += i; - gdb_con->buf_cnt -= i; + buf_p += i; + buf_cnt -= i; if (done) break; } if (count > *len) { LOG_ERROR("packet buffer too small"); - return ERROR_GDB_BUFFER_TOO_SMALL; + retval = ERROR_GDB_BUFFER_TOO_SMALL; + break; } - if ((retval = gdb_get_char(connection, &character)) != ERROR_OK) - return retval; + retval = gdb_get_char_fast(connection, &character, &buf_p, &buf_cnt); + if (retval != ERROR_OK) + break; if (character == '#') break; @@ -515,8 +568,11 @@ static __inline__ int fetch_packet(struct connection *connection, int *checksum_ /* data transmitted in binary mode (X packet) * uses 0x7d as escape character */ my_checksum += character & 0xff; - if ((retval = gdb_get_char(connection, &character)) != ERROR_OK) - return retval; + + retval = gdb_get_char_fast(connection, &character, &buf_p, &buf_cnt); + if (retval != ERROR_OK) + break; + my_checksum += character & 0xff; buffer[count++] = (character ^ 0x20) & 0xff; } @@ -527,6 +583,12 @@ static __inline__ int fetch_packet(struct connection *connection, int *checksum_ } } + gdb_con->buf_p = buf_p; + gdb_con->buf_cnt = buf_cnt; + + if (retval != ERROR_OK) + return retval; + *len = count; if ((retval = gdb_get_char(connection, &character)) != ERROR_OK) @@ -568,7 +630,7 @@ int gdb_get_packet_inner(struct connection *connection, char *buffer, int *len) break; case '+': /* gdb sends a dummy ack '+' at every remote connect - see remote_start_remote (remote.c) - * incase anyone tries to debug why they receive this warning every time */ + * in case anyone tries to debug why they receive this warning every time */ LOG_WARNING("acknowledgment received, but no packet pending"); break; case '-': @@ -774,7 +836,7 @@ int gdb_new_connection(struct connection *connection) gdb_actual_connections++; LOG_DEBUG("New GDB Connection: %d, Target %s, state: %s", gdb_actual_connections, - gdb_service->target->cmd_name, + target_name(gdb_service->target), target_state_name(gdb_service->target)); return ERROR_OK; @@ -792,7 +854,7 @@ int gdb_connection_closed(struct connection *connection) gdb_actual_connections--; LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d", - gdb_service->target->cmd_name, + target_name(gdb_service->target), target_state_name(gdb_service->target), gdb_actual_connections); @@ -859,7 +921,7 @@ static int gdb_reg_pos(struct target *target, int pos, int len) * register might be non-divisible by 8(a byte), in which * case an entire byte is shown. * - * NB! the format on the wire is the target endianess + * NB! the format on the wire is the target endianness * * The format of reg->value is little endian * @@ -881,7 +943,7 @@ void gdb_str_to_target(struct target *target, char *tstr, struct reg *reg) } } -static int hextoint(char c) +static int hextoint(int c) { if (c>='0'&&c<='9') { @@ -2138,10 +2200,10 @@ int gdb_input_inner(struct connection *connection) watchpoint_clear_target(gdb_service->target); command_run_linef(connection->cmd_ctx, "ocd_gdb_restart %s", - target->cmd_name); + target_name(target)); break; default: - /* ignore unkown packets */ + /* ignore unknown packets */ LOG_DEBUG("ignoring 0x%2.2x packet", packet[0]); gdb_put_packet(connection, NULL, 0); break; @@ -2189,55 +2251,73 @@ int gdb_input(struct connection *connection) return ERROR_OK; } -int gdb_init(void) +static int gdb_target_start(struct target *target, uint16_t port) { - struct gdb_service *gdb_service; - struct target *target = all_targets; + bool use_pipes = 0 == port; + struct gdb_service *gdb_service = malloc(sizeof(struct gdb_service)); + if (NULL == gdb_service) + return -ENOMEM; - if (!target) - { - LOG_WARNING("no gdb ports allocated as no target has been specified"); - return ERROR_OK; - } + gdb_service->target = target; + + add_service("gdb", use_pipes ? CONNECTION_PIPE : CONNECTION_TCP, + port, 1, &gdb_new_connection, &gdb_input, + &gdb_connection_closed, gdb_service); + + const char *name = target_name(target); + if (use_pipes) + LOG_DEBUG("gdb service for target '%s' using pipes", name); + else + LOG_DEBUG("gdb service for target '%s' on TCP port %u", name, port); + return ERROR_OK; +} +/* FIXME static */ +int gdb_target_add_one(struct target *target) +{ if (gdb_port == 0 && server_use_pipes == 0) { LOG_INFO("gdb port disabled"); return ERROR_OK; } + if (0 == gdb_port_next) + gdb_port_next = gdb_port; - if (server_use_pipes) + bool use_pipes = server_use_pipes; + static bool server_started_with_pipes = false; + if (server_started_with_pipes) { - /* only a single gdb connection when using a pipe */ + LOG_WARNING("gdb service permits one target when using pipes"); + if (0 == gdb_port) + return ERROR_OK; - gdb_service = malloc(sizeof(struct gdb_service)); - gdb_service->target = target; + use_pipes = false; + } - add_service("gdb", CONNECTION_PIPE, 0, 1, gdb_new_connection, gdb_input, gdb_connection_closed, gdb_service); + int e = gdb_target_start(target, use_pipes ? 0 : gdb_port_next); + if (ERROR_OK == e) + { + server_started_with_pipes |= use_pipes; + gdb_port_next++; + } + return e; +} - LOG_DEBUG("gdb service for target %s using pipes", - target_get_name(target)); +int gdb_target_add_all(struct target *target) +{ + if (NULL == target) + { + LOG_WARNING("gdb services need one or more targets defined"); + return ERROR_OK; } - else + + while (NULL != target) { - unsigned short port = gdb_port; + int retval = gdb_target_add_one(target); + if (ERROR_OK != retval) + return retval; - while (target) - { - gdb_service = malloc(sizeof(struct gdb_service)); - gdb_service->target = target; - - add_service("gdb", CONNECTION_TCP, - port, 1, - gdb_new_connection, gdb_input, - gdb_connection_closed, gdb_service); - - LOG_DEBUG("gdb service for target %s at TCP port %i", - target_get_name(target), - port); - target = target->next; - port++; - } + target = target->next; } return ERROR_OK; @@ -2265,7 +2345,10 @@ COMMAND_HANDLER(handle_gdb_sync_command) /* daemon configuration command gdb_port */ COMMAND_HANDLER(handle_gdb_port_command) { - return CALL_COMMAND_HANDLER(server_port_command, &gdb_port); + int retval = CALL_COMMAND_HANDLER(server_port_command, &gdb_port); + if (ERROR_OK == retval) + gdb_port_next = gdb_port; + return retval; } COMMAND_HANDLER(handle_gdb_memory_map_command) @@ -2320,7 +2403,7 @@ COMMAND_HANDLER(handle_gdb_breakpoint_override_command) LOG_USER("force %s breakpoints", (gdb_breakpoint_override_type == BKPT_HARD)?"hard":"soft"); } else { - LOG_USER("breakpoint type is not overriden"); + LOG_USER("breakpoint type is not overridden"); } return ERROR_OK; @@ -2329,7 +2412,7 @@ COMMAND_HANDLER(handle_gdb_breakpoint_override_command) static const struct command_registration gdb_command_handlers[] = { { .name = "gdb_sync", - .handler = &handle_gdb_sync_command, + .handler = handle_gdb_sync_command, .mode = COMMAND_ANY, .help = "next stepi will return immediately allowing " "GDB to fetch register state without affecting " @@ -2337,39 +2420,41 @@ static const struct command_registration gdb_command_handlers[] = { }, { .name = "gdb_port", - .handler = &handle_gdb_port_command, + .handler = handle_gdb_port_command, .mode = COMMAND_ANY, - .help = "daemon configuration command gdb_port", - .usage = "", + .help = "Display or specify base port on which to listen " + "for incoming GDB connections. " + "No arguments reports GDB port; zero disables.", + .usage = "[port_num]", }, { .name = "gdb_memory_map", - .handler = &handle_gdb_memory_map_command, + .handler = handle_gdb_memory_map_command, .mode = COMMAND_CONFIG, .help = "enable or disable memory map", - .usage = "enable|disable" + .usage = "('enable'|'disable')" }, { .name = "gdb_flash_program", - .handler = &handle_gdb_flash_program_command, + .handler = handle_gdb_flash_program_command, .mode = COMMAND_CONFIG, .help = "enable or disable flash program", - .usage = "enable|disable" + .usage = "('enable'|'disable')" }, { .name = "gdb_report_data_abort", - .handler = &handle_gdb_report_data_abort_command, + .handler = handle_gdb_report_data_abort_command, .mode = COMMAND_CONFIG, .help = "enable or disable reporting data aborts", - .usage = "enable|disable" + .usage = "('enable'|'disable')" }, { .name = "gdb_breakpoint_override", - .handler = &handle_gdb_breakpoint_override_command, + .handler = handle_gdb_breakpoint_override_command, .mode = COMMAND_EXEC, - .help = "force type of breakpoint " - "used by gdb 'break' commands.", - .usage = "hard|soft|disable", + .help = "Display or specify type of breakpoint " + "to be used by gdb 'break' commands.", + .usage = "('hard'|'soft'|'disable')" }, COMMAND_REGISTRATION_DONE };