aboutsummaryrefslogtreecommitdiff
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
parent80d412bafc03ce9a0418a2b98de2668b0f8de0e6 (diff)
downloadriscv-openocd-0a4c8990c29e61fd0c2796486519cdb256b8da3b.zip
riscv-openocd-0a4c8990c29e61fd0c2796486519cdb256b8da3b.tar.gz
riscv-openocd-0a4c8990c29e61fd0c2796486519cdb256b8da3b.tar.bz2
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>
-rw-r--r--src/server/gdb_server.c249
-rw-r--r--src/target/nds32.c320
-rw-r--r--src/target/nds32.h16
-rw-r--r--src/target/nds32_v3.c3
-rw-r--r--src/target/nds32_v3_common.c155
-rw-r--r--src/target/nds32_v3m.c3
-rw-r--r--src/target/target.c42
-rw-r--r--src/target/target.h28
-rw-r--r--src/target/target_type.h9
9 files changed, 776 insertions, 49 deletions
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index 8eacf8c..14925a2 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]);
diff --git a/src/target/nds32.c b/src/target/nds32.c
index 95a249d..c4bd63a 100644
--- a/src/target/nds32.c
+++ b/src/target/nds32.c
@@ -1645,6 +1645,10 @@ int nds32_init_arch_info(struct target *target, struct nds32 *nds32)
nds32->keep_target_edm_ctl = false;
nds32->word_access_mem = false;
nds32->virtual_hosting = false;
+ nds32->hit_syscall = false;
+ nds32->active_syscall_id = NDS32_SYSCALL_UNDEFINED;
+ nds32->virtual_hosting_errno = 0;
+ nds32->virtual_hosting_ctrl_c = false;
nds32_reg_init();
@@ -1772,13 +1776,24 @@ int nds32_step(struct target *target, int current,
ir14_value &= ~(0x1 << 31);
nds32_set_mapped_reg(nds32, IR14, ir14_value);
+ /* check hit_syscall before leave_debug_state() because
+ * leave_debug_state() may clear hit_syscall flag */
+ bool no_step = false;
+ if (nds32->hit_syscall)
+ /* step after hit_syscall should be ignored because
+ * leave_debug_state will step implicitly to skip the
+ * syscall */
+ no_step = true;
+
/********* TODO: maybe create another function to handle this part */
CHECK_RETVAL(nds32->leave_debug_state(nds32, true));
CHECK_RETVAL(target_call_event_callbacks(target, TARGET_EVENT_RESUMED));
- struct aice_port_s *aice = target_to_aice(target);
- if (ERROR_OK != aice_step(aice))
- return ERROR_FAIL;
+ if (no_step == false) {
+ struct aice_port_s *aice = target_to_aice(target);
+ if (ERROR_OK != aice_step(aice))
+ return ERROR_FAIL;
+ }
/* save state */
CHECK_RETVAL(nds32->enter_debug_state(nds32, true));
@@ -1878,6 +1893,12 @@ int nds32_examine_debug_reason(struct nds32 *nds32)
uint32_t reason;
struct target *target = nds32->target;
+ if (nds32->hit_syscall == true) {
+ LOG_DEBUG("Hit syscall breakpoint");
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ return ERROR_OK;
+ }
+
nds32->get_debug_reason(nds32, &reason);
LOG_DEBUG("nds32 examines debug reason: %s", nds32_debug_type_name[reason]);
@@ -2110,8 +2131,11 @@ int nds32_resume(struct target *target, int current,
CHECK_RETVAL(nds32->leave_debug_state(nds32, true));
CHECK_RETVAL(target_call_event_callbacks(target, TARGET_EVENT_RESUMED));
- struct aice_port_s *aice = target_to_aice(target);
- aice_run(aice);
+ if (nds32->virtual_hosting_ctrl_c == false) {
+ struct aice_port_s *aice = target_to_aice(target);
+ aice_run(aice);
+ } else
+ nds32->virtual_hosting_ctrl_c = false;
target->debug_reason = DBG_REASON_NOTHALTED;
if (!debug_execution)
@@ -2239,6 +2263,292 @@ int nds32_init(struct nds32 *nds32)
return ERROR_OK;
}
+int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
+{
+ /* fill syscall parameters to file-I/O info */
+ if (NULL == fileio_info) {
+ LOG_ERROR("Target has not initial file-I/O data structure");
+ return ERROR_FAIL;
+ }
+
+ struct nds32 *nds32 = target_to_nds32(target);
+ uint32_t value_ir6;
+ uint32_t syscall_id;
+
+ if (nds32->hit_syscall == false)
+ return ERROR_FAIL;
+
+ nds32_get_mapped_reg(nds32, IR6, &value_ir6);
+ syscall_id = (value_ir6 >> 16) & 0x7FFF;
+ nds32->active_syscall_id = syscall_id;
+
+ LOG_DEBUG("hit syscall ID: 0x%x", syscall_id);
+
+ /* free previous identifier storage */
+ if (NULL != fileio_info->identifier) {
+ free(fileio_info->identifier);
+ fileio_info->identifier = NULL;
+ }
+
+ switch (syscall_id) {
+ case NDS32_SYSCALL_EXIT:
+ fileio_info->identifier = (char *)malloc(5);
+ sprintf(fileio_info->identifier, "exit");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ break;
+ case NDS32_SYSCALL_OPEN:
+ {
+ uint8_t filename[256];
+ fileio_info->identifier = (char *)malloc(5);
+ sprintf(fileio_info->identifier, "open");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ /* reserve fileio_info->param_2 for length of path */
+ nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
+ nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_4));
+
+ target->type->read_buffer(target, fileio_info->param_1,
+ 256, filename);
+ fileio_info->param_2 = strlen((char *)filename) + 1;
+ }
+ break;
+ case NDS32_SYSCALL_CLOSE:
+ fileio_info->identifier = (char *)malloc(6);
+ sprintf(fileio_info->identifier, "close");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ break;
+ case NDS32_SYSCALL_READ:
+ fileio_info->identifier = (char *)malloc(5);
+ sprintf(fileio_info->identifier, "read");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
+ nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
+ break;
+ case NDS32_SYSCALL_WRITE:
+ fileio_info->identifier = (char *)malloc(6);
+ sprintf(fileio_info->identifier, "write");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
+ nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
+ break;
+ case NDS32_SYSCALL_LSEEK:
+ fileio_info->identifier = (char *)malloc(6);
+ sprintf(fileio_info->identifier, "lseek");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
+ nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
+ break;
+ case NDS32_SYSCALL_UNLINK:
+ {
+ uint8_t filename[256];
+ fileio_info->identifier = (char *)malloc(7);
+ sprintf(fileio_info->identifier, "unlink");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ /* reserve fileio_info->param_2 for length of path */
+
+ target->type->read_buffer(target, fileio_info->param_1,
+ 256, filename);
+ fileio_info->param_2 = strlen((char *)filename) + 1;
+ }
+ break;
+ case NDS32_SYSCALL_RENAME:
+ {
+ uint8_t filename[256];
+ fileio_info->identifier = (char *)malloc(7);
+ sprintf(fileio_info->identifier, "rename");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ /* reserve fileio_info->param_2 for length of old path */
+ nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
+ /* reserve fileio_info->param_4 for length of new path */
+
+ target->type->read_buffer(target, fileio_info->param_1,
+ 256, filename);
+ fileio_info->param_2 = strlen((char *)filename) + 1;
+
+ target->type->read_buffer(target, fileio_info->param_3,
+ 256, filename);
+ fileio_info->param_4 = strlen((char *)filename) + 1;
+ }
+ break;
+ case NDS32_SYSCALL_FSTAT:
+ fileio_info->identifier = (char *)malloc(6);
+ sprintf(fileio_info->identifier, "fstat");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
+ break;
+ case NDS32_SYSCALL_STAT:
+ {
+ uint8_t filename[256];
+ fileio_info->identifier = (char *)malloc(5);
+ sprintf(fileio_info->identifier, "stat");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ /* reserve fileio_info->param_2 for length of old path */
+ nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
+
+ target->type->read_buffer(target, fileio_info->param_1,
+ 256, filename);
+ fileio_info->param_2 = strlen((char *)filename) + 1;
+ }
+ break;
+ case NDS32_SYSCALL_GETTIMEOFDAY:
+ fileio_info->identifier = (char *)malloc(13);
+ sprintf(fileio_info->identifier, "gettimeofday");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
+ break;
+ case NDS32_SYSCALL_ISATTY:
+ fileio_info->identifier = (char *)malloc(7);
+ sprintf(fileio_info->identifier, "isatty");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ break;
+ case NDS32_SYSCALL_SYSTEM:
+ {
+ uint8_t command[256];
+ fileio_info->identifier = (char *)malloc(7);
+ sprintf(fileio_info->identifier, "system");
+ nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
+ /* reserve fileio_info->param_2 for length of old path */
+
+ target->type->read_buffer(target, fileio_info->param_1,
+ 256, command);
+ fileio_info->param_2 = strlen((char *)command) + 1;
+ }
+ break;
+ case NDS32_SYSCALL_ERRNO:
+ fileio_info->identifier = (char *)malloc(6);
+ sprintf(fileio_info->identifier, "errno");
+ nds32_set_mapped_reg(nds32, R0, nds32->virtual_hosting_errno);
+ break;
+ default:
+ fileio_info->identifier = (char *)malloc(8);
+ sprintf(fileio_info->identifier, "unknown");
+ break;
+ }
+
+ return ERROR_OK;
+}
+
+int nds32_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, bool ctrl_c)
+{
+ LOG_DEBUG("syscall return code: 0x%x, errno: 0x%x, ctrl_c: %s",
+ retcode, fileio_errno, ctrl_c ? "true" : "false");
+
+ struct nds32 *nds32 = target_to_nds32(target);
+
+ nds32_set_mapped_reg(nds32, R0, (uint32_t)retcode);
+
+ nds32->virtual_hosting_errno = fileio_errno;
+ nds32->virtual_hosting_ctrl_c = ctrl_c;
+ nds32->active_syscall_id = NDS32_SYSCALL_UNDEFINED;
+
+ return ERROR_OK;
+}
+
+int nds32_gdb_fileio_write_memory(struct nds32 *nds32, uint32_t address,
+ uint32_t size, const uint8_t *buffer)
+{
+ if ((NDS32_SYSCALL_FSTAT == nds32->active_syscall_id) ||
+ (NDS32_SYSCALL_STAT == nds32->active_syscall_id)) {
+ /* If doing GDB file-I/O, target should convert 'struct stat'
+ * from gdb-format to target-format */
+ uint8_t stat_buffer[NDS32_STRUCT_STAT_SIZE];
+ /* st_dev 2 */
+ stat_buffer[0] = buffer[3];
+ stat_buffer[1] = buffer[2];
+ /* st_ino 2 */
+ stat_buffer[2] = buffer[7];
+ stat_buffer[3] = buffer[6];
+ /* st_mode 4 */
+ stat_buffer[4] = buffer[11];
+ stat_buffer[5] = buffer[10];
+ stat_buffer[6] = buffer[9];
+ stat_buffer[7] = buffer[8];
+ /* st_nlink 2 */
+ stat_buffer[8] = buffer[15];
+ stat_buffer[9] = buffer[16];
+ /* st_uid 2 */
+ stat_buffer[10] = buffer[19];
+ stat_buffer[11] = buffer[18];
+ /* st_gid 2 */
+ stat_buffer[12] = buffer[23];
+ stat_buffer[13] = buffer[22];
+ /* st_rdev 2 */
+ stat_buffer[14] = buffer[27];
+ stat_buffer[15] = buffer[26];
+ /* st_size 4 */
+ stat_buffer[16] = buffer[35];
+ stat_buffer[17] = buffer[34];
+ stat_buffer[18] = buffer[33];
+ stat_buffer[19] = buffer[32];
+ /* st_atime 4 */
+ stat_buffer[20] = buffer[55];
+ stat_buffer[21] = buffer[54];
+ stat_buffer[22] = buffer[53];
+ stat_buffer[23] = buffer[52];
+ /* st_spare1 4 */
+ stat_buffer[24] = 0;
+ stat_buffer[25] = 0;
+ stat_buffer[26] = 0;
+ stat_buffer[27] = 0;
+ /* st_mtime 4 */
+ stat_buffer[28] = buffer[59];
+ stat_buffer[29] = buffer[58];
+ stat_buffer[30] = buffer[57];
+ stat_buffer[31] = buffer[56];
+ /* st_spare2 4 */
+ stat_buffer[32] = 0;
+ stat_buffer[33] = 0;
+ stat_buffer[34] = 0;
+ stat_buffer[35] = 0;
+ /* st_ctime 4 */
+ stat_buffer[36] = buffer[63];
+ stat_buffer[37] = buffer[62];
+ stat_buffer[38] = buffer[61];
+ stat_buffer[39] = buffer[60];
+ /* st_spare3 4 */
+ stat_buffer[40] = 0;
+ stat_buffer[41] = 0;
+ stat_buffer[42] = 0;
+ stat_buffer[43] = 0;
+ /* st_blksize 4 */
+ stat_buffer[44] = buffer[43];
+ stat_buffer[45] = buffer[42];
+ stat_buffer[46] = buffer[41];
+ stat_buffer[47] = buffer[40];
+ /* st_blocks 4 */
+ stat_buffer[48] = buffer[51];
+ stat_buffer[49] = buffer[50];
+ stat_buffer[50] = buffer[49];
+ stat_buffer[51] = buffer[48];
+ /* st_spare4 8 */
+ stat_buffer[52] = 0;
+ stat_buffer[53] = 0;
+ stat_buffer[54] = 0;
+ stat_buffer[55] = 0;
+ stat_buffer[56] = 0;
+ stat_buffer[57] = 0;
+ stat_buffer[58] = 0;
+ stat_buffer[59] = 0;
+
+ return nds32_write_buffer(nds32->target, address, NDS32_STRUCT_STAT_SIZE, stat_buffer);
+ } else if (NDS32_SYSCALL_GETTIMEOFDAY == nds32->active_syscall_id) {
+ /* If doing GDB file-I/O, target should convert 'struct timeval'
+ * from gdb-format to target-format */
+ uint8_t timeval_buffer[NDS32_STRUCT_TIMEVAL_SIZE];
+ timeval_buffer[0] = buffer[3];
+ timeval_buffer[1] = buffer[2];
+ timeval_buffer[2] = buffer[1];
+ timeval_buffer[3] = buffer[0];
+ timeval_buffer[4] = buffer[11];
+ timeval_buffer[5] = buffer[10];
+ timeval_buffer[6] = buffer[9];
+ timeval_buffer[7] = buffer[8];
+
+ return nds32_write_buffer(nds32->target, address, NDS32_STRUCT_TIMEVAL_SIZE, timeval_buffer);
+ }
+
+ return nds32_write_buffer(nds32->target, address, size, buffer);
+}
+
int nds32_reset_halt(struct nds32 *nds32)
{
LOG_INFO("reset halt as init");
diff --git a/src/target/nds32.h b/src/target/nds32.h
index b7e787c..8acd915 100644
--- a/src/target/nds32.h
+++ b/src/target/nds32.h
@@ -284,6 +284,18 @@ struct nds32 {
/** Flag reporting whether virtual hosting is active. */
bool virtual_hosting;
+ /** Flag reporting whether continue/step hits syscall or not */
+ bool hit_syscall;
+
+ /** Value to be returned by virtual hosting SYS_ERRNO request. */
+ int virtual_hosting_errno;
+
+ /** Flag reporting whether syscall is aborted */
+ bool virtual_hosting_ctrl_c;
+
+ /** Record syscall ID for other operations to do special processing for target */
+ int active_syscall_id;
+
/** Flag reporting whether global stop is active. */
bool global_stop;
@@ -404,6 +416,10 @@ extern int nds32_resume(struct target *target, int current,
uint32_t address, int handle_breakpoints, int debug_execution);
extern int nds32_assert_reset(struct target *target);
extern int nds32_init(struct nds32 *nds32);
+extern int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info);
+extern int nds32_gdb_fileio_write_memory(struct nds32 *nds32, uint32_t address,
+ uint32_t size, const uint8_t *buffer);
+extern int nds32_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, bool ctrl_c);
extern int nds32_reset_halt(struct nds32 *nds32);
extern int nds32_login(struct nds32 *nds32);
diff --git a/src/target/nds32_v3.c b/src/target/nds32_v3.c
index 868260d..766e5cc 100644
--- a/src/target/nds32_v3.c
+++ b/src/target/nds32_v3.c
@@ -520,4 +520,7 @@ struct target_type nds32_v3_target = {
.target_create = nds32_v3_target_create,
.init_target = nds32_v3_init_target,
.examine = nds32_v3_examine,
+
+ .get_gdb_fileio_info = nds32_get_gdb_fileio_info,
+ .gdb_fileio_end = nds32_gdb_fileio_end,
};
diff --git a/src/target/nds32_v3_common.c b/src/target/nds32_v3_common.c
index 2fbd1a3..f0cd77d 100644
--- a/src/target/nds32_v3_common.c
+++ b/src/target/nds32_v3_common.c
@@ -29,6 +29,18 @@
#include "nds32_aice.h"
#include "nds32_v3_common.h"
+static struct breakpoint syscall_breakpoint = {
+ 0x80,
+ 0,
+ 4,
+ BKPT_SOFT,
+ 0,
+ NULL,
+ NULL,
+ 0x515CA11,
+ 0,
+};
+
static struct nds32_v3_common_callback *v3_common_callback;
static int nds32_v3_register_mapping(struct nds32 *nds32, int reg_no)
@@ -80,6 +92,22 @@ static int nds32_v3_debug_entry(struct nds32 *nds32, bool enable_watchpoint)
if (enable_watchpoint)
CHECK_RETVAL(v3_common_callback->deactivate_hardware_watchpoint(nds32->target));
+ if (nds32->virtual_hosting) {
+ if (syscall_breakpoint.set) {
+ /** disable virtual hosting */
+
+ /* remove breakpoint at syscall entry */
+ target_remove_breakpoint(nds32->target, &syscall_breakpoint);
+ syscall_breakpoint.set = 0;
+
+ uint32_t value_pc;
+ nds32_get_mapped_reg(nds32, PC, &value_pc);
+ if (value_pc == syscall_breakpoint.address)
+ /** process syscall for virtual hosting */
+ nds32->hit_syscall = true;
+ }
+ }
+
if (ERROR_OK != nds32_examine_debug_reason(nds32)) {
nds32->target->state = backup_state;
@@ -132,6 +160,74 @@ static int nds32_v3_leave_debug_state(struct nds32 *nds32, bool enable_watchpoin
*/
CHECK_RETVAL(nds32_restore_context(target));
+ if (nds32->virtual_hosting) {
+ /** enable virtual hosting */
+ uint32_t value_ir3;
+ uint32_t entry_size;
+ uint32_t syscall_address;
+
+ /* get syscall entry address */
+ nds32_get_mapped_reg(nds32, IR3, &value_ir3);
+ entry_size = 0x4 << (((value_ir3 >> 14) & 0x3) << 1);
+ syscall_address = (value_ir3 & 0xFFFF0000) + entry_size * 8; /* The index of SYSCALL is 8 */
+
+ if (nds32->hit_syscall) {
+ /* single step to skip syscall entry */
+ /* use IRET to skip syscall */
+ struct aice_port_s *aice = target_to_aice(target);
+ uint32_t value_ir9;
+ uint32_t value_ir6;
+ uint32_t syscall_id;
+
+ nds32_get_mapped_reg(nds32, IR6, &value_ir6);
+ syscall_id = (value_ir6 >> 16) & 0x7FFF;
+
+ if (syscall_id == NDS32_SYSCALL_EXIT) {
+ /* If target hits exit syscall, do not use IRET to skip handler. */
+ aice_step(aice);
+ } else {
+ /* use api->read/write_reg to skip nds32 register cache */
+ uint32_t value_dimbr;
+ aice_read_debug_reg(aice, NDS_EDM_SR_DIMBR, &value_dimbr);
+ aice_write_register(aice, IR11, value_dimbr + 0xC);
+
+ aice_read_register(aice, IR9, &value_ir9);
+ value_ir9 += 4; /* syscall is always 4 bytes */
+ aice_write_register(aice, IR9, value_ir9);
+
+ /* backup hardware breakpoint 0 */
+ uint32_t backup_bpa, backup_bpam, backup_bpc;
+ aice_read_debug_reg(aice, NDS_EDM_SR_BPA0, &backup_bpa);
+ aice_read_debug_reg(aice, NDS_EDM_SR_BPAM0, &backup_bpam);
+ aice_read_debug_reg(aice, NDS_EDM_SR_BPC0, &backup_bpc);
+
+ /* use hardware breakpoint 0 to stop cpu after skipping syscall */
+ aice_write_debug_reg(aice, NDS_EDM_SR_BPA0, value_ir9);
+ aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0, 0);
+ aice_write_debug_reg(aice, NDS_EDM_SR_BPC0, 0xA);
+
+ /* Execute two IRET.
+ * First IRET is used to quit debug mode.
+ * Second IRET is used to quit current syscall. */
+ uint32_t dim_inst[4] = {NOP, NOP, IRET, IRET};
+ aice_execute(aice, dim_inst, 4);
+
+ /* restore origin hardware breakpoint 0 */
+ aice_write_debug_reg(aice, NDS_EDM_SR_BPA0, backup_bpa);
+ aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0, backup_bpam);
+ aice_write_debug_reg(aice, NDS_EDM_SR_BPC0, backup_bpc);
+ }
+
+ nds32->hit_syscall = false;
+ }
+
+ /* insert breakpoint at syscall entry */
+ syscall_breakpoint.address = syscall_address;
+ syscall_breakpoint.type = BKPT_SOFT;
+ syscall_breakpoint.set = 1;
+ target_add_breakpoint(target, &syscall_breakpoint);
+ }
+
/* enable polling */
jtag_poll_set_enabled(true);
@@ -398,7 +494,27 @@ int nds32_v3_read_buffer(struct target *target, uint32_t address,
else
return ERROR_FAIL;
- return nds32_read_buffer(target, address, size, buffer);
+ int result;
+ struct aice_port_s *aice = target_to_aice(target);
+ /* give arbitrary initial value to avoid warning messages */
+ enum nds_memory_access origin_access_channel = NDS_MEMORY_ACC_CPU;
+
+ if (nds32->hit_syscall) {
+ /* Use bus mode to access memory during virtual hosting */
+ origin_access_channel = memory->access_channel;
+ memory->access_channel = NDS_MEMORY_ACC_BUS;
+ aice_memory_access(aice, NDS_MEMORY_ACC_BUS);
+ }
+
+ result = nds32_read_buffer(target, address, size, buffer);
+
+ if (nds32->hit_syscall) {
+ /* Restore access_channel after virtual hosting */
+ memory->access_channel = origin_access_channel;
+ aice_memory_access(aice, origin_access_channel);
+ }
+
+ return result;
}
int nds32_v3_write_buffer(struct target *target, uint32_t address,
@@ -436,6 +552,24 @@ int nds32_v3_write_buffer(struct target *target, uint32_t address,
else
return ERROR_FAIL;
+ if (nds32->hit_syscall) {
+ /* Use bus mode to access memory during virtual hosting */
+ struct aice_port_s *aice = target_to_aice(target);
+ enum nds_memory_access origin_access_channel;
+ int result;
+
+ origin_access_channel = memory->access_channel;
+ memory->access_channel = NDS_MEMORY_ACC_BUS;
+ aice_memory_access(aice, NDS_MEMORY_ACC_BUS);
+
+ result = nds32_gdb_fileio_write_memory(nds32, address, size, buffer);
+
+ memory->access_channel = origin_access_channel;
+ aice_memory_access(aice, origin_access_channel);
+
+ return result;
+ }
+
return nds32_write_buffer(target, address, size, buffer);
}
@@ -474,10 +608,26 @@ int nds32_v3_read_memory(struct target *target, uint32_t address,
else
return ERROR_FAIL;
+ struct aice_port_s *aice = target_to_aice(target);
+ /* give arbitrary initial value to avoid warning messages */
+ enum nds_memory_access origin_access_channel = NDS_MEMORY_ACC_CPU;
int result;
+ if (nds32->hit_syscall) {
+ /* Use bus mode to access memory during virtual hosting */
+ origin_access_channel = memory->access_channel;
+ memory->access_channel = NDS_MEMORY_ACC_BUS;
+ aice_memory_access(aice, NDS_MEMORY_ACC_BUS);
+ }
+
result = nds32_read_memory(target, address, size, count, buffer);
+ if (nds32->hit_syscall) {
+ /* Restore access_channel after virtual hosting */
+ memory->access_channel = origin_access_channel;
+ aice_memory_access(aice, origin_access_channel);
+ }
+
return result;
}
@@ -527,5 +677,8 @@ int nds32_v3_init_target(struct command_context *cmd_ctx,
nds32_init(nds32);
+ target->fileio_info = malloc(sizeof(struct gdb_fileio_info));
+ target->fileio_info->identifier = NULL;
+
return ERROR_OK;
}
diff --git a/src/target/nds32_v3m.c b/src/target/nds32_v3m.c
index c37798a..d72d986 100644
--- a/src/target/nds32_v3m.c
+++ b/src/target/nds32_v3m.c
@@ -508,4 +508,7 @@ struct target_type nds32_v3m_target = {
.target_create = nds32_v3m_target_create,
.init_target = nds32_v3_init_target,
.examine = nds32_v3m_examine,
+
+ .get_gdb_fileio_info = nds32_get_gdb_fileio_info,
+ .gdb_fileio_end = nds32_gdb_fileio_end,
};
diff --git a/src/target/target.c b/src/target/target.c
index 4c31fbe..442f0d9 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -68,6 +68,10 @@ static int target_array2mem(Jim_Interp *interp, struct target *target,
static int target_mem2array(Jim_Interp *interp, struct target *target,
int argc, Jim_Obj * const *argv);
static int target_register_user_commands(struct command_context *cmd_ctx);
+static int target_get_gdb_fileio_info_default(struct target *target,
+ struct gdb_fileio_info *fileio_info);
+static int target_gdb_fileio_end_default(struct target *target, int retcode,
+ int fileio_errno, bool ctrl_c);
/* targets */
extern struct target_type arm7tdmi_target;
@@ -1065,6 +1069,24 @@ int target_step(struct target *target,
return target->type->step(target, current, address, handle_breakpoints);
}
+int target_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target %s is not halted", target->cmd_name);
+ return ERROR_TARGET_NOT_HALTED;
+ }
+ return target->type->get_gdb_fileio_info(target, fileio_info);
+}
+
+int target_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, bool ctrl_c)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_WARNING("target %s is not halted", target->cmd_name);
+ return ERROR_TARGET_NOT_HALTED;
+ }
+ return target->type->gdb_fileio_end(target, retcode, fileio_errno, ctrl_c);
+}
+
/**
* Reset the @c examined flag for the given target.
* Pure paranoia -- targets are zeroed on allocation.
@@ -1151,6 +1173,12 @@ static int target_init_one(struct command_context *cmd_ctx,
if (target->type->bulk_write_memory == NULL)
target->type->bulk_write_memory = target_bulk_write_memory_default;
+ if (target->type->get_gdb_fileio_info == NULL)
+ target->type->get_gdb_fileio_info = target_get_gdb_fileio_info_default;
+
+ if (target->type->gdb_fileio_end == NULL)
+ target->type->gdb_fileio_end = target_gdb_fileio_end_default;
+
return ERROR_OK;
}
@@ -1700,6 +1728,20 @@ int target_arch_state(struct target *target)
return retval;
}
+static int target_get_gdb_fileio_info_default(struct target *target,
+ struct gdb_fileio_info *fileio_info)
+{
+ LOG_ERROR("Not implemented: %s", __func__);
+ return ERROR_FAIL;
+}
+
+static int target_gdb_fileio_end_default(struct target *target,
+ int retcode, int fileio_errno, bool ctrl_c)
+{
+ LOG_ERROR("Not implemented: %s", __func__);
+ return ERROR_OK;
+}
+
/* Single aligned words are guaranteed to use 16 or 32 bit access
* mode respectively, otherwise data is handled as quickly as
* possible
diff --git a/src/target/target.h b/src/target/target.h
index 09895bb..ee282b1 100644
--- a/src/target/target.h
+++ b/src/target/target.h
@@ -41,6 +41,7 @@ struct watchpoint;
struct mem_param;
struct reg_param;
struct target_list;
+struct gdb_fileio_info;
/*
* TARGET_UNKNOWN = 0: we don't know anything about the target yet
@@ -191,6 +192,9 @@ struct target {
* the target attached to the gdb is changing dynamically by changing
* gdb_service->target pointer */
struct gdb_service *gdb_service;
+
+ /* file-I/O information for host to do syscall */
+ struct gdb_fileio_info *fileio_info;
};
struct target_list {
@@ -198,6 +202,14 @@ struct target_list {
struct target_list *next;
};
+struct gdb_fileio_info {
+ char *identifier;
+ uint32_t param_1;
+ uint32_t param_2;
+ uint32_t param_3;
+ uint32_t param_4;
+};
+
/** Returns the instance-specific name of the specified target. */
static inline const char *target_name(struct target *target)
{
@@ -534,6 +546,22 @@ int target_blank_check_memory(struct target *target,
uint32_t address, uint32_t size, uint32_t *blank);
int target_wait_state(struct target *target, enum target_state state, int ms);
+/**
+ * Obtain file-I/O information from target for GDB to do syscall.
+ *
+ * This routine is a wrapper for target->type->get_gdb_fileio_info.
+ */
+int target_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info);
+
+/**
+ * Pass GDB file-I/O response to target after finishing host syscall.
+ *
+ * This routine is a wrapper for target->type->gdb_fileio_end.
+ */
+int target_gdb_fileio_end(struct target *target, int retcode, int fileio_errno, bool ctrl_c);
+
+
+
/** Return the *name* of this targets current state */
const char *target_state_name(struct target *target);
diff --git a/src/target/target_type.h b/src/target/target_type.h
index 0b8d5da..21439b6 100644
--- a/src/target/target_type.h
+++ b/src/target/target_type.h
@@ -264,6 +264,15 @@ struct target_type {
* circumstances.
*/
int (*check_reset)(struct target *target);
+
+ /* get GDB file-I/O parameters from target
+ */
+ int (*get_gdb_fileio_info)(struct target *target, struct gdb_fileio_info *fileio_info);
+
+ /* pass GDB file-I/O response to target
+ */
+ int (*gdb_fileio_end)(struct target *target, int retcode, int fileio_errno, bool ctrl_c);
+
};
#endif /* TARGET_TYPE_H */