From 0a4c8990c29e61fd0c2796486519cdb256b8da3b Mon Sep 17 00:00:00 2001 From: Hsiangkai Wang Date: Wed, 2 Jan 2013 12:02:00 +0800 Subject: [PATCH] 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 Reviewed-on: http://openocd.zylin.com/1102 Tested-by: jenkins Reviewed-by: Spencer Oliver --- src/server/gdb_server.c | 249 ++++++++++++++++++++++----- src/target/nds32.c | 320 ++++++++++++++++++++++++++++++++++- src/target/nds32.h | 16 ++ src/target/nds32_v3.c | 3 + src/target/nds32_v3_common.c | 155 ++++++++++++++++- src/target/nds32_v3m.c | 3 + src/target/target.c | 42 +++++ src/target/target.h | 28 +++ src/target/target_type.h | 9 + 9 files changed, 776 insertions(+), 49 deletions(-) diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index 8eacf8c38..14925a2bf 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 95a249d05..c4bd63aad 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 b7e787cf0..8acd915f0 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 868260dc9..766e5ccbb 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 2fbd1a378..f0cd77d23 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 c37798afb..d72d98691 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 4c31fbea2..442f0d9f1 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 09895bbab..ee282b193 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 0b8d5daa1..21439b656 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 */