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>
This commit is contained in:
Hsiangkai Wang
2013-01-02 12:02:00 +08:00
committed by Spencer Oliver
parent 80d412bafc
commit 0a4c8990c2
9 changed files with 776 additions and 49 deletions

View File

@@ -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]);