forked from auracaster/openocd
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:
committed by
Spencer Oliver
parent
80d412bafc
commit
0a4c8990c2
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user