aboutsummaryrefslogtreecommitdiff
path: root/src/server/gdb_server.c
diff options
context:
space:
mode:
authorHsiangkai Wang <hsiangkai@gmail.com>2013-01-02 12:02:00 +0800
committerSpencer Oliver <spen@spen-soft.co.uk>2013-08-07 21:01:25 +0000
commit0a4c8990c29e61fd0c2796486519cdb256b8da3b (patch)
tree56a4d7b47b392b7da4fa1ad08a4d5261c3619a0e /src/server/gdb_server.c
parent80d412bafc03ce9a0418a2b98de2668b0f8de0e6 (diff)
gdb_server: support File-I/O Remote Protocol Extension
The File I/O remote protocol extension allows the target to use the host's file system and console I/O to perform various system calls. To use the function, targets need to prepare two callback functions: * get_gdb_finish_info: to get file I/O parameters from target * gdb_fileio_end: pass file I/O response to target As target is halted, gdb_server will try to get file-I/O information from target through target_get_gdb_fileio_info(). If the callback function returns ERROR_OK, gdb_server will initiate a file-I/O request to gdb. After gdb finishes system call, gdb will pass response of the system call to target through target_gdb_fileio_end() and continue to run(continue or step). To implement the function, I add a new data structure in struct target, called struct gdb_fileio_info, to record file I/O name and parameters. Details refer to GDB manual "File-I/O Remote Protocol Extension" Change-Id: I7f4d45e7c9e967b6d898dc79ba01d86bc46315d3 Signed-off-by: Hsiangkai Wang <hsiangkai@gmail.com> Reviewed-on: http://openocd.zylin.com/1102 Tested-by: jenkins Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Diffstat (limited to 'src/server/gdb_server.c')
-rw-r--r--src/server/gdb_server.c249
1 files changed, 206 insertions, 43 deletions
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index 8eacf8c3..14925a2b 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -125,6 +125,9 @@ static int gdb_report_data_abort;
/* disabled by default */
static int gdb_use_target_description;
+/* current processing free-run type, used by file-I/O */
+static char gdb_running_type;
+
static int gdb_last_signal(struct target *target)
{
switch (target->debug_reason) {
@@ -691,6 +694,142 @@ static int gdb_output(struct command_context *context, const char *line)
return ERROR_OK;
}
+static void gdb_signal_reply(struct target *target, struct connection *connection)
+{
+ struct gdb_connection *gdb_connection = connection->priv;
+ char sig_reply[20];
+ char stop_reason[20];
+ int sig_reply_len;
+ int signal_var;
+
+ if (gdb_connection->ctrl_c) {
+ signal_var = 0x2;
+ gdb_connection->ctrl_c = 0;
+ } else
+ signal_var = gdb_last_signal(target);
+
+ stop_reason[0] = '\0';
+ if (target->debug_reason == DBG_REASON_WATCHPOINT) {
+ enum watchpoint_rw hit_wp_type;
+ uint32_t hit_wp_address;
+
+ if (watchpoint_hit(target, &hit_wp_type, &hit_wp_address) == ERROR_OK) {
+
+ switch (hit_wp_type) {
+ case WPT_WRITE:
+ snprintf(stop_reason, sizeof(stop_reason),
+ "watch:%08x;", hit_wp_address);
+ break;
+ case WPT_READ:
+ snprintf(stop_reason, sizeof(stop_reason),
+ "rwatch:%08x;", hit_wp_address);
+ break;
+ case WPT_ACCESS:
+ snprintf(stop_reason, sizeof(stop_reason),
+ "awatch:%08x;", hit_wp_address);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s",
+ signal_var, stop_reason);
+
+ gdb_put_packet(connection, sig_reply, sig_reply_len);
+ gdb_connection->frontend_state = TARGET_HALTED;
+ rtos_update_threads(target);
+}
+
+static void gdb_fileio_reply(struct target *target, struct connection *connection)
+{
+ struct gdb_connection *gdb_connection = connection->priv;
+ char fileio_command[256];
+ int command_len;
+ bool program_exited = false;
+
+ if (strcmp(target->fileio_info->identifier, "open") == 0)
+ sprintf(fileio_command, "F%s,%x/%x,%x,%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2,
+ target->fileio_info->param_3,
+ target->fileio_info->param_4);
+ else if (strcmp(target->fileio_info->identifier, "close") == 0)
+ sprintf(fileio_command, "F%s,%x", target->fileio_info->identifier,
+ target->fileio_info->param_1);
+ else if (strcmp(target->fileio_info->identifier, "read") == 0)
+ sprintf(fileio_command, "F%s,%x,%x,%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2,
+ target->fileio_info->param_3);
+ else if (strcmp(target->fileio_info->identifier, "write") == 0)
+ sprintf(fileio_command, "F%s,%x,%x,%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2,
+ target->fileio_info->param_3);
+ else if (strcmp(target->fileio_info->identifier, "lseek") == 0)
+ sprintf(fileio_command, "F%s,%x,%x,%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2,
+ target->fileio_info->param_3);
+ else if (strcmp(target->fileio_info->identifier, "rename") == 0)
+ sprintf(fileio_command, "F%s,%x/%x,%x/%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2,
+ target->fileio_info->param_3,
+ target->fileio_info->param_4);
+ else if (strcmp(target->fileio_info->identifier, "unlink") == 0)
+ sprintf(fileio_command, "F%s,%x/%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2);
+ else if (strcmp(target->fileio_info->identifier, "stat") == 0)
+ sprintf(fileio_command, "F%s,%x/%x,%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2,
+ target->fileio_info->param_3);
+ else if (strcmp(target->fileio_info->identifier, "fstat") == 0)
+ sprintf(fileio_command, "F%s,%x,%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2);
+ else if (strcmp(target->fileio_info->identifier, "gettimeofday") == 0)
+ sprintf(fileio_command, "F%s,%x,%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2);
+ else if (strcmp(target->fileio_info->identifier, "isatty") == 0)
+ sprintf(fileio_command, "F%s,%x", target->fileio_info->identifier,
+ target->fileio_info->param_1);
+ else if (strcmp(target->fileio_info->identifier, "system") == 0)
+ sprintf(fileio_command, "F%s,%x/%x", target->fileio_info->identifier,
+ target->fileio_info->param_1,
+ target->fileio_info->param_2);
+ else if (strcmp(target->fileio_info->identifier, "exit") == 0) {
+ /* If target hits exit syscall, report to GDB the program is terminated.
+ * In addition, let target run its own exit syscall handler. */
+ program_exited = true;
+ sprintf(fileio_command, "W%02x", target->fileio_info->param_1);
+ } else {
+ LOG_DEBUG("Unknown syscall: %s", target->fileio_info->identifier);
+
+ /* encounter unknown syscall, continue */
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ target_resume(target, 1, 0x0, 0, 0);
+ return;
+ }
+
+ command_len = strlen(fileio_command);
+ gdb_put_packet(connection, fileio_command, command_len);
+
+ if (program_exited) {
+ /* Use target_resume() to let target run its own exit syscall handler. */
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ target_resume(target, 1, 0x0, 0, 0);
+ } else {
+ gdb_connection->frontend_state = TARGET_HALTED;
+ rtos_update_threads(target);
+ }
+}
+
static void gdb_frontend_halted(struct target *target, struct connection *connection)
{
struct gdb_connection *gdb_connection = connection->priv;
@@ -705,52 +844,14 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec
* that are to be ignored.
*/
if (gdb_connection->frontend_state == TARGET_RUNNING) {
- char sig_reply[20];
- char stop_reason[20];
- int sig_reply_len;
- int signal_var;
-
/* stop forwarding log packets! */
log_remove_callback(gdb_log_callback, connection);
- if (gdb_connection->ctrl_c) {
- signal_var = 0x2;
- gdb_connection->ctrl_c = 0;
- } else
- signal_var = gdb_last_signal(target);
-
- stop_reason[0] = '\0';
- if (target->debug_reason == DBG_REASON_WATCHPOINT) {
- enum watchpoint_rw hit_wp_type;
- uint32_t hit_wp_address;
-
- if (watchpoint_hit(target, &hit_wp_type, &hit_wp_address) == ERROR_OK) {
-
- switch (hit_wp_type) {
- case WPT_WRITE:
- snprintf(stop_reason, sizeof(stop_reason),
- "watch:%08x;", hit_wp_address);
- break;
- case WPT_READ:
- snprintf(stop_reason, sizeof(stop_reason),
- "rwatch:%08x;", hit_wp_address);
- break;
- case WPT_ACCESS:
- snprintf(stop_reason, sizeof(stop_reason),
- "awatch:%08x;", hit_wp_address);
- break;
- default:
- break;
- }
- }
- }
-
- sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s",
- signal_var, stop_reason);
-
- gdb_put_packet(connection, sig_reply, sig_reply_len);
- gdb_connection->frontend_state = TARGET_HALTED;
- rtos_update_threads(target);
+ /* check fileio first */
+ if (target_get_gdb_fileio_info(target, target->fileio_info) == ERROR_OK)
+ gdb_fileio_reply(target, connection);
+ else
+ gdb_signal_reply(target, connection);
}
}
@@ -1391,6 +1492,7 @@ static int gdb_step_continue_packet(struct connection *connection,
} else
current = 1;
+ gdb_running_type = packet[0];
if (packet[0] == 'c') {
LOG_DEBUG("continue");
/* resume at current address, don't handle breakpoints, not debugging */
@@ -2315,6 +2417,54 @@ static int gdb_detach(struct connection *connection)
return gdb_put_packet(connection, "OK", 2);
}
+/* The format of 'F' response packet is
+ * Fretcode,errno,Ctrl-C flag;call-specific attachment
+ */
+static int gdb_fileio_response_packet(struct connection *connection,
+ char *packet, int packet_size)
+{
+ struct target *target = get_target_from_connection(connection);
+ char *separator;
+ char *parsing_point;
+ int fileio_retcode = strtoul(packet + 1, &separator, 16);
+ int fileio_errno = 0;
+ bool fileio_ctrl_c = false;
+ int retval;
+
+ LOG_DEBUG("-");
+
+ if (*separator == ',') {
+ parsing_point = separator + 1;
+ fileio_errno = strtoul(parsing_point, &separator, 16);
+ if (*separator == ',') {
+ if (*(separator + 1) == 'C') {
+ /* TODO: process ctrl-c */
+ fileio_ctrl_c = true;
+ }
+ }
+ }
+
+ LOG_DEBUG("File-I/O response, retcode: 0x%x, errno: 0x%x, ctrl-c: %s",
+ fileio_retcode, fileio_errno, fileio_ctrl_c ? "true" : "false");
+
+ retval = target_gdb_fileio_end(target, fileio_retcode, fileio_errno, fileio_ctrl_c);
+ if (retval != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* After File-I/O ends, keep continue or step */
+ if (gdb_running_type == 'c')
+ retval = target_resume(target, 1, 0x0, 0, 0);
+ else if (gdb_running_type == 's')
+ retval = target_step(target, 1, 0x0, 0);
+ else
+ retval = ERROR_FAIL;
+
+ if (retval != ERROR_OK)
+ return ERROR_FAIL;
+
+ return ERROR_OK;
+}
+
static void gdb_log_callback(void *priv, const char *file, unsigned line,
const char *function, const char *string)
{
@@ -2541,6 +2691,19 @@ static int gdb_input_inner(struct connection *connection)
gdb_write_smp_packet(connection, packet, packet_size);
break;
+ case 'F':
+ /* File-I/O extension */
+ /* After gdb uses host-side syscall to complete target file
+ * I/O, gdb sends host-side syscall return value to target
+ * by 'F' packet.
+ * The format of 'F' response packet is
+ * Fretcode,errno,Ctrl-C flag;call-specific attachment
+ */
+ gdb_con->frontend_state = TARGET_RUNNING;
+ log_add_callback(gdb_log_callback, connection);
+ gdb_fileio_response_packet(connection, packet, packet_size);
+ break;
+
default:
/* ignore unknown packets */
LOG_DEBUG("ignoring 0x%2.2x packet", packet[0]);