#include <target/breakpoints.h>
#include <target/target_request.h>
#include <target/register.h>
+#include <target/target.h>
+#include <target/target_type.h>
#include "server.h"
#include <flash/nor/core.h>
#include "gdb_server.h"
static void gdb_log_callback(void *priv, const char *file, unsigned line,
const char *function, const char *string);
+static void gdb_sig_halted(struct connection *connection);
+
/* 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;
current_thread[0] = '\0';
if (target->rtos != NULL) {
- snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";", target->rtos->current_thread);
+ struct target *ct;
+ snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";",
+ target->rtos->current_thread);
target->rtos->current_threadid = target->rtos->current_thread;
+ target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
+ signal_var = gdb_last_signal(ct);
}
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
breakpoint_clear_target(target);
watchpoint_clear_target(target);
- /* clean previous rtos session if supported*/
- if ((target->rtos) && (target->rtos->type->clean))
- target->rtos->type->clean(target);
+ if (target->rtos) {
+ /* clean previous rtos session if supported*/
+ if (target->rtos->type->clean)
+ target->rtos->type->clean(target);
+
+ /* update threads */
+ rtos_update_threads(target);
+ }
/* remove the initial ACK from the incoming buffer */
retval = gdb_get_char(connection, &initial_ack);
}
gdb_actual_connections++;
- LOG_DEBUG("New GDB Connection: %d, Target %s, state: %s",
+ log_printf_lf(all_targets->next != NULL ? LOG_LVL_INFO : LOG_LVL_DEBUG,
+ __FILE__, __LINE__, __func__,
+ "New GDB Connection: %d, Target %s, state: %s",
gdb_actual_connections,
target_name(target),
target_state_name(target));
char gdb_reply[10];
char *separator;
uint32_t checksum;
- uint32_t addr = 0;
+ target_addr_t addr = 0;
uint32_t len = 0;
/* skip command character */
packet += 5;
- addr = strtoul(packet, &separator, 16);
+ addr = strtoull(packet, &separator, 16);
if (*separator != ',') {
LOG_ERROR("incomplete read memory packet received, dropping connection");
&buffer,
&pos,
&size,
- "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+",
+ "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+;vContSupported+",
(GDB_BUFFER_SIZE - 1),
((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-',
(gdb_target_desc_supported == 1) ? '+' : '-');
return ERROR_OK;
}
+static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size)
+{
+ struct gdb_connection *gdb_connection = connection->priv;
+ struct target *target = get_target_from_connection(connection);
+ const char *parse = packet;
+ int retval;
+
+ /* query for vCont supported */
+ if (parse[0] == '?') {
+ if (target->type->step != NULL) {
+ /* gdb doesn't accept c without C and s without S */
+ gdb_put_packet(connection, "vCont;c;C;s;S", 13);
+ return true;
+ }
+ return false;
+ }
+
+ if (parse[0] == ';') {
+ ++parse;
+ --packet_size;
+ }
+
+ /* simple case, a continue packet */
+ if (parse[0] == 'c') {
+ LOG_DEBUG("target %s continue", target_name(target));
+ log_add_callback(gdb_log_callback, connection);
+ retval = target_resume(target, 1, 0, 0, 0);
+ if (retval == ERROR_TARGET_NOT_HALTED)
+ LOG_INFO("target %s was not halted when resume was requested", target_name(target));
+
+ /* poll target in an attempt to make its internal state consistent */
+ if (retval != ERROR_OK) {
+ retval = target_poll(target);
+ if (retval != ERROR_OK)
+ LOG_DEBUG("error polling target %s after failed resume", target_name(target));
+ }
+
+ /*
+ * We don't report errors to gdb here, move frontend_state to
+ * TARGET_RUNNING to stay in sync with gdb's expectation of the
+ * target state
+ */
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+
+ return true;
+ }
+
+ /* single-step or step-over-breakpoint */
+ if (parse[0] == 's') {
+ if (strncmp(parse, "s:", 2) == 0) {
+ struct target *ct = target;
+ int current_pc = 1;
+ int64_t thread_id;
+ char *endp;
+
+ parse += 2;
+ packet_size -= 2;
+
+ thread_id = strtoll(parse, &endp, 16);
+ if (endp != NULL) {
+ packet_size -= endp - parse;
+ parse = endp;
+ }
+
+ if (target->rtos != NULL)
+ target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
+
+ if (parse[0] == ';') {
+ ++parse;
+ --packet_size;
+
+ if (parse[0] == 'c') {
+ parse += 1;
+ packet_size -= 1;
+
+ /* check if thread-id follows */
+ if (parse[0] == ':') {
+ int64_t tid;
+ parse += 1;
+ packet_size -= 1;
+
+ tid = strtoll(parse, &endp, 16);
+ if (tid == thread_id) {
+ /*
+ * Special case: only step a single thread (core),
+ * keep the other threads halted. Currently, only
+ * aarch64 target understands it. Other target types don't
+ * care (nobody checks the actual value of 'current')
+ * and it doesn't really matter. This deserves
+ * a symbolic constant and a formal interface documentation
+ * at a later time.
+ */
+ LOG_DEBUG("request to step current core only");
+ /* uncomment after checking that indeed other targets are safe */
+ /*current_pc = 2;*/
+ }
+ }
+ }
+ }
+
+ LOG_DEBUG("target %s single-step thread %"PRId64, target_name(ct), thread_id);
+ log_add_callback(gdb_log_callback, connection);
+ target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
+
+ /* support for gdb_sync command */
+ if (gdb_connection->sync) {
+ gdb_connection->sync = false;
+ if (ct->state == TARGET_HALTED) {
+ LOG_WARNING("stepi ignored. GDB will now fetch the register state " \
+ "from the target.");
+ gdb_sig_halted(connection);
+ log_remove_callback(gdb_log_callback, connection);
+ } else
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ return true;
+ }
+
+ retval = target_step(ct, current_pc, 0, 0);
+ if (retval == ERROR_TARGET_NOT_HALTED)
+ LOG_INFO("target %s was not halted when step was requested", target_name(ct));
+
+ /* if step was successful send a reply back to gdb */
+ if (retval == ERROR_OK) {
+ retval = target_poll(ct);
+ if (retval != ERROR_OK)
+ LOG_DEBUG("error polling target %s after successful step", target_name(ct));
+ /* send back signal information */
+ gdb_signal_reply(ct, connection);
+ /* stop forwarding log packets! */
+ log_remove_callback(gdb_log_callback, connection);
+ } else
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ } else {
+ LOG_ERROR("Unknown vCont packet");
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
static int gdb_v_packet(struct connection *connection,
char const *packet, int packet_size)
{
target = get_target_from_connection(connection);
+ if (strncmp(packet, "vCont", 5) == 0) {
+ bool handled;
+
+ packet += 5;
+ packet_size -= 5;
+
+ handled = gdb_handle_vcont_packet(connection, packet, packet_size);
+ if (!handled)
+ gdb_put_packet(connection, "", 0);
+
+ return ERROR_OK;
+ }
+
/* if flash programming disabled - send a empty reply */
if (gdb_flash_program == 0) {
if (gdb_con->ctrl_c) {
if (target->state == TARGET_RUNNING) {
- retval = target_halt(target);
+ struct target *t = target;
+ if (target->rtos)
+ target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
+ retval = target_halt(t);
+ if (retval == ERROR_OK)
+ retval = target_poll(t);
if (retval != ERROR_OK)
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
gdb_con->ctrl_c = 0;
if (!*end) {
if (parse_long(gdb_port_next, &portnumber) == ERROR_OK) {
free(gdb_port_next);
- gdb_port_next = alloc_printf("%d", portnumber+1);
+ if (portnumber) {
+ gdb_port_next = alloc_printf("%d", portnumber+1);
+ } else {
+ /* Don't increment if gdb_port is 0, since we're just
+ * trying to allocate an unused port. */
+ gdb_port_next = strdup("0");
+ }
}
}
}