enum gdb_output_flag {
/* GDB doesn't accept 'O' packets */
GDB_OUTPUT_NO,
+ /* GDB doesn't accept 'O' packets but accepts notifications */
+ GDB_OUTPUT_NOTIF,
/* GDB accepts 'O' packets */
GDB_OUTPUT_ALL,
};
enum target_state frontend_state;
struct image *vflash_image;
bool closed;
+ /* set to prevent re-entrance from log messages during gdb_get_packet()
+ * and gdb_put_packet(). */
bool busy;
int noack_mode;
/* set flag to true if you want the next stepi to return immediately.
char *thread_list;
/* flag to mask the output from gdb_log_callback() */
enum gdb_output_flag output_flag;
+ /* Unique index for this GDB connection. */
+ unsigned int unique_index;
};
#if 0
/* set if we are sending a memory map to gdb
* via qXfer:memory-map:read packet */
/* enabled by default*/
-static int gdb_use_memory_map = 1;
+static bool gdb_use_memory_map = true;
/* enabled by default*/
-static int gdb_flash_program = 1;
+static bool gdb_flash_program = true;
/* if set, data aborts cause an error to be reported in memory read packets
* see the code in gdb_read_memory_packet() for further explanations.
/* set if we are sending target descriptions to gdb
* via qXfer:features:read packet */
/* enabled by default */
-static int gdb_use_target_description = 1;
+static bool gdb_use_target_description = true;
/* current processing free-run type, used by file-I/O */
static char gdb_running_type;
return;
struct target *target = get_target_from_connection(connection);
+ struct gdb_connection *gdb_connection = connection->priv;
/* Avoid dumping non-printable characters to the terminal */
const unsigned packet_len = strlen(packet);
if (packet_prefix_printable) {
const unsigned int prefix_len = colon - packet + 1; /* + 1 to include the ':' */
const unsigned int payload_len = packet_len - prefix_len;
- LOG_TARGET_DEBUG(target, "received packet: %.*s<binary-data-%u-bytes>", prefix_len,
- packet, payload_len);
+ LOG_TARGET_DEBUG(target, "{%d} received packet: %.*s<binary-data-%u-bytes>",
+ gdb_connection->unique_index, prefix_len, packet, payload_len);
} else {
- LOG_TARGET_DEBUG(target, "received packet: <binary-data-%u-bytes>", packet_len);
+ LOG_TARGET_DEBUG(target, "{%d} received packet: <binary-data-%u-bytes>",
+ gdb_connection->unique_index, packet_len);
}
} else {
/* All chars printable, dump the packet as is */
- LOG_TARGET_DEBUG(target, "received packet: %s", packet);
+ LOG_TARGET_DEBUG(target, "{%d} received packet: %s", gdb_connection->unique_index, packet);
}
}
return;
struct target *target = get_target_from_connection(connection);
+ struct gdb_connection *gdb_connection = connection->priv;
if (find_nonprint_char(packet_buf, packet_len))
- LOG_TARGET_DEBUG(target, "sending packet: $<binary-data-%u-bytes>#%2.2x",
- packet_len, checksum);
+ LOG_TARGET_DEBUG(target, "{%d} sending packet: $<binary-data-%u-bytes>#%2.2x",
+ gdb_connection->unique_index, packet_len, checksum);
else
- LOG_TARGET_DEBUG(target, "sending packet: $%.*s#%2.2x", packet_len, packet_buf,
- checksum);
+ LOG_TARGET_DEBUG(target, "{%d} sending packet: $%.*s#%2.2x",
+ gdb_connection->unique_index, packet_len, packet_buf, checksum);
}
static int gdb_put_packet_inner(struct connection *connection,
struct target *target;
int retval;
int initial_ack;
+ static unsigned int next_unique_id = 1;
target = get_target_from_connection(connection);
connection->priv = gdb_connection;
gdb_connection->target_desc.tdesc_length = 0;
gdb_connection->thread_list = NULL;
gdb_connection->output_flag = GDB_OUTPUT_NO;
-
- /* send ACK to GDB for debug request */
- gdb_write(connection, "+", 1);
+ gdb_connection->unique_index = next_unique_id++;
/* output goes through gdb connection */
command_set_output_handler(connection->cmd_ctx, gdb_output, connection);
}
}
- gdb_actual_connections++;
log_printf_lf(all_targets->next ? LOG_LVL_INFO : LOG_LVL_DEBUG,
__FILE__, __LINE__, __func__,
"New GDB Connection: %d, Target %s, state: %s",
- gdb_actual_connections,
+ gdb_connection->unique_index,
target_name(target),
target_state_name(target));
if (!target_was_examined(target)) {
LOG_ERROR("Target %s not examined yet, refuse gdb connection %d!",
- target_name(target), gdb_actual_connections);
- gdb_actual_connections--;
+ target_name(target), gdb_connection->unique_index);
return ERROR_TARGET_NOT_EXAMINED;
}
+ gdb_actual_connections++;
if (target->state != TARGET_HALTED)
LOG_WARNING("GDB connection %d on target %s not halted",
log_remove_callback(gdb_log_callback, connection);
gdb_actual_connections--;
- LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
+ LOG_DEBUG("{%d} GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
+ gdb_connection->unique_index,
target_name(target),
target_state_name(target),
gdb_actual_connections);
return ERROR_OK;
}
-/* We don't have to worry about the default 2 second timeout for GDB packets,
- * because GDB breaks up large memory reads into smaller reads.
- */
static int gdb_read_memory_packet(struct connection *connection,
char const *packet, int packet_size)
{
return ERROR_OK;
}
-static int gdb_target_description_supported(struct target *target, int *supported)
+static int gdb_target_description_supported(struct target *target, bool *supported)
{
int retval = ERROR_OK;
struct reg **reg_list = NULL;
if (supported) {
if (architecture || feature_list_size)
- *supported = 1;
+ *supported = true;
else
- *supported = 0;
+ *supported = false;
}
error:
len = strtoul(separator + 1, NULL, 16);
+ gdb_connection->output_flag = GDB_OUTPUT_NOTIF;
retval = target_checksum_memory(target, addr, len, &checksum);
+ gdb_connection->output_flag = GDB_OUTPUT_NO;
if (retval == ERROR_OK) {
snprintf(gdb_reply, 10, "C%8.8" PRIx32 "", checksum);
char *buffer = NULL;
int pos = 0;
int size = 0;
- int gdb_target_desc_supported = 0;
+ bool gdb_target_desc_supported = false;
/* we need to test that the target supports target descriptions */
retval = gdb_target_description_supported(target, &gdb_target_desc_supported);
if (retval != ERROR_OK) {
LOG_INFO("Failed detecting Target Description Support, disabling");
- gdb_target_desc_supported = 0;
+ gdb_target_desc_supported = false;
}
/* support may be disabled globally */
- if (gdb_use_target_description == 0) {
+ if (!gdb_use_target_description) {
if (gdb_target_desc_supported)
LOG_WARNING("Target Descriptions Supported, but disabled");
- gdb_target_desc_supported = 0;
+ gdb_target_desc_supported = false;
}
xml_printf(&retval,
&size,
"PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+;vContSupported+",
GDB_BUFFER_SIZE,
- ((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-',
- (gdb_target_desc_supported == 1) ? '+' : '-');
+ (gdb_use_memory_map && (flash_get_bank_count() > 0)) ? '+' : '-',
+ gdb_target_desc_supported ? '+' : '-');
if (retval != ERROR_OK) {
gdb_send_error(connection, 01);
/* if flash programming disabled - send a empty reply */
- if (gdb_flash_program == 0) {
+ if (!gdb_flash_program) {
gdb_put_packet(connection, "", 0);
return ERROR_OK;
}
if (strncmp(packet, "vFlashErase:", 12) == 0) {
- unsigned long addr;
+ target_addr_t addr;
unsigned long length;
char const *parse = packet + 12;
return ERROR_SERVER_REMOTE_CLOSED;
}
- addr = strtoul(parse, (char **)&parse, 16);
+ addr = strtoull(parse, (char **)&parse, 16);
if (*(parse++) != ',' || *parse == '\0') {
LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
if (strncmp(packet, "vFlashWrite:", 12) == 0) {
int retval;
- unsigned long addr;
+ target_addr_t addr;
unsigned long length;
char const *parse = packet + 12;
LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
return ERROR_SERVER_REMOTE_CLOSED;
}
- addr = strtoul(parse, (char **)&parse, 16);
+
+ addr = strtoull(parse, (char **)&parse, 16);
if (*(parse++) != ':') {
LOG_ERROR("incomplete vFlashErase packet received, dropping connection");
return ERROR_SERVER_REMOTE_CLOSED;
if (strncmp(packet, "vFlashDone", 10) == 0) {
uint32_t written;
+ /* GDB command 'flash-erase' does not send a vFlashWrite,
+ * so nothing to write here. */
+ if (!gdb_connection->vflash_image) {
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+ }
+
/* process the flashing buffer. No need to erase as GDB
* always issues a vFlashErase first. */
target_call_event_callbacks(target,
struct connection *connection = priv;
struct gdb_connection *gdb_con = connection->priv;
- if (gdb_con->output_flag == GDB_OUTPUT_NO)
+ if (gdb_con->output_flag != GDB_OUTPUT_ALL)
/* No out allowed */
return;
retval = gdb_set_register_packet(connection, packet, packet_size);
break;
case 'm':
+ gdb_con->output_flag = GDB_OUTPUT_NOTIF;
retval = gdb_read_memory_packet(connection, packet, packet_size);
+ gdb_con->output_flag = GDB_OUTPUT_NO;
break;
case 'M':
+ gdb_con->output_flag = GDB_OUTPUT_NOTIF;
retval = gdb_write_memory_packet(connection, packet, packet_size);
+ gdb_con->output_flag = GDB_OUTPUT_NO;
break;
case 'z':
case 'Z':
retval = gdb_detach(connection);
break;
case 'X':
+ gdb_con->output_flag = GDB_OUTPUT_NOTIF;
retval = gdb_write_memory_binary_packet(connection, packet, packet_size);
- if (retval != ERROR_OK)
- return retval;
+ gdb_con->output_flag = GDB_OUTPUT_NO;
break;
case 'k':
if (gdb_con->extended_protocol) {
return ERROR_OK;
}
+/*
+ * Send custom notification packet as keep-alive during memory read/write.
+ *
+ * From gdb 7.0 (released 2009-10-06) an unknown notification received during
+ * memory read/write would be silently dropped.
+ * Before gdb 7.0 any character, with exclusion of "+-$", would be considered
+ * as junk and ignored.
+ * In both cases the reception will reset the timeout counter in gdb, thus
+ * working as a keep-alive.
+ * Check putpkt_binary() and getpkt_sane() in gdb commit
+ * 74531fed1f2d662debc2c209b8b3faddceb55960
+ *
+ * Enable remote debug in gdb with 'set debug remote 1' to either dump the junk
+ * characters in gdb pre-7.0 and the notification from gdb 7.0.
+ */
+static void gdb_async_notif(struct connection *connection)
+{
+ static unsigned char count;
+ unsigned char checksum = 0;
+ char buf[22];
+
+ int len = sprintf(buf, "%%oocd_keepalive:%2.2x", count++);
+ for (int i = 1; i < len; i++)
+ checksum += buf[i];
+ len += sprintf(buf + len, "#%2.2x", checksum);
+
+#ifdef _DEBUG_GDB_IO_
+ LOG_DEBUG("sending packet '%s'", buf);
+#endif
+
+ gdb_write(connection, buf, len);
+}
+
static void gdb_keep_client_alive(struct connection *connection)
{
struct gdb_connection *gdb_con = connection->priv;
- if (gdb_con->busy) {
- /* do not send packets, retry asap */
- return;
- }
-
switch (gdb_con->output_flag) {
case GDB_OUTPUT_NO:
/* no need for keep-alive */
break;
+ case GDB_OUTPUT_NOTIF:
+ /* send asynchronous notification */
+ gdb_async_notif(connection);
+ break;
case GDB_OUTPUT_ALL:
/* send an empty O packet */
gdb_output_con(connection, "");