diff --git a/doc/openocd.texi b/doc/openocd.texi index 4b964ed56..24928f6be 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -8092,6 +8092,30 @@ interacting with remote files or displaying console messages in the debugger. @end deffn +@deffn Command {arm semihosting_resexit} [@option{enable}|@option{disable}] +@cindex ARM semihosting +Enable resumable SEMIHOSTING_SYS_EXIT. + +When SEMIHOSTING_SYS_EXIT is called outside a debug session, +things are simple, the openocd process calls exit() and passes +the value returned by the target. + +When SEMIHOSTING_SYS_EXIT is called during a debug session, +by default execution returns to the debugger, leaving the +debugger in a HALT state, similar to the state entered when +encountering a break. + +In some use cases, it is useful to have SEMIHOSTING_SYS_EXIT +return normally, as any semihosting call, and do not break +to the debugger. +The standard allows this to happen, but the condition +to trigger it is a bit obscure ("by performing an RDI_Execute +request or equivalent"). + +To make the SEMIHOSTING_SYS_EXIT call return normally, enable +this option (default: disabled). +@end deffn + @section ARMv4 and ARMv5 Architecture @cindex ARMv4 @cindex ARMv5 diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index d329bfeb3..f54db9e35 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -793,64 +793,64 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio bool program_exited = false; if (strcmp(target->fileio_info->identifier, "open") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 ",%" PRIx64, 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,%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1); else if (strcmp(target->fileio_info->identifier, "read") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, 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,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, 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,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, 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,%" PRIx32 "/%" PRIx32 ",%" PRIx32 "/%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 "/%" PRIx64, 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,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, 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,%" PRIx32 "/%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64, 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,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, 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,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, 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,%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1); else if (strcmp(target->fileio_info->identifier, "system") == 0) - sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier, + sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, 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%02" PRIx32, target->fileio_info->param_1); + sprintf(fileio_command, "W%02" PRIx64, target->fileio_info->param_1); } else { LOG_DEBUG("Unknown syscall: %s", target->fileio_info->identifier); diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 206b28148..fcc23adbe 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -39,6 +39,7 @@ TARGET_CORE_SRC = \ %D%/target.c \ %D%/target_request.c \ %D%/testee.c \ + %D%/semihosting_common.c \ %D%/smp.c ARMV4_5_SRC = \ @@ -210,6 +211,7 @@ INTEL_IA32_SRC = \ %D%/nds32_v3.h \ %D%/nds32_v3m.h \ %D%/nds32_aice.h \ + %D%/semihosting_common.h \ %D%/stm8.h \ %D%/lakemont.h \ %D%/x86_32_common.h \ diff --git a/src/target/arm.h b/src/target/arm.h index dd25d53be..316ff9ab8 100644 --- a/src/target/arm.h +++ b/src/target/arm.h @@ -8,6 +8,9 @@ * Copyright (C) 2009 by Øyvind Harboe * oyvind.harboe@zylin.com * + * Copyright (C) 2018 by Liviu Ionescu + * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -28,7 +31,6 @@ #include #include "target.h" - /** * @file * Holds the interface to ARM cores. @@ -181,32 +183,11 @@ struct arm { /** Flag reporting armv6m based core. */ bool is_armv6m; - /** Flag reporting whether semihosting is active. */ - bool is_semihosting; - - /** Flag reporting whether semihosting fileio is active. */ - bool is_semihosting_fileio; - - /** Flag reporting whether semihosting fileio operation is active. */ - bool semihosting_hit_fileio; - /** Floating point or VFP version, 0 if disabled. */ int arm_vfp_version; - /** Current semihosting operation. */ - int semihosting_op; - - /** Current semihosting result. */ - int semihosting_result; - - /** Value to be returned by semihosting SYS_ERRNO request. */ - int semihosting_errno; - int (*setup_semihosting)(struct target *target, int enable); - /** Semihosting command line. */ - char *semihosting_cmdline; - /** Backpointer to the target. */ struct target *target; diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c index f31f901f0..57f3139c2 100644 --- a/src/target/arm_semihosting.c +++ b/src/target/arm_semihosting.c @@ -8,6 +8,9 @@ * Copyright (C) 2016 by Square, Inc. * * Steven Stallion * * * + * Copyright (C) 2018 by Liviu Ionescu * + * * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -52,21 +55,6 @@ #include #include -static const int open_modeflags[12] = { - O_RDONLY, - O_RDONLY | O_BINARY, - O_RDWR, - O_RDWR | O_BINARY, - O_WRONLY | O_CREAT | O_TRUNC, - O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, - O_RDWR | O_CREAT | O_TRUNC, - O_RDWR | O_CREAT | O_TRUNC | O_BINARY, - O_WRONLY | O_CREAT | O_APPEND, - O_WRONLY | O_CREAT | O_APPEND | O_BINARY, - O_RDWR | O_CREAT | O_APPEND, - O_RDWR | O_CREAT | O_APPEND | O_BINARY -}; - static int post_result(struct target *target) { struct arm *arm = target_to_arm(target); @@ -79,7 +67,7 @@ static int post_result(struct target *target) uint32_t spsr; /* return value in R0 */ - buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result); + buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result); arm->core_cache->reg_list[0].dirty = 1; /* LR --> PC */ @@ -105,523 +93,13 @@ static int post_result(struct target *target) * bkpt instruction */ /* return result in R0 */ - buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result); + buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result); arm->core_cache->reg_list[0].dirty = 1; } return ERROR_OK; } -static int do_semihosting(struct target *target) -{ - struct arm *arm = target_to_arm(target); - struct gdb_fileio_info *fileio_info = target->fileio_info; - uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32); - uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32); - uint8_t params[16]; - int retval; - - /* - * TODO: lots of security issues are not considered yet, such as: - * - no validation on target provided file descriptors - * - no safety checks on opened/deleted/renamed file paths - * Beware the target app you use this support with. - * - * TODO: unsupported semihosting fileio operations could be - * implemented if we had a small working area at our disposal. - */ - switch ((arm->semihosting_op = r0)) { - case 0x01: /* SYS_OPEN */ - retval = target_read_memory(target, r1, 4, 3, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a = target_buffer_get_u32(target, params+0); - uint32_t m = target_buffer_get_u32(target, params+4); - uint32_t l = target_buffer_get_u32(target, params+8); - uint8_t fn[256]; - retval = target_read_memory(target, a, 1, l, fn); - if (retval != ERROR_OK) - return retval; - fn[l] = 0; - if (arm->is_semihosting_fileio) { - if (strcmp((char *)fn, ":tt") == 0) - arm->semihosting_result = 0; - else { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "open"; - fileio_info->param_1 = a; - fileio_info->param_2 = l; - fileio_info->param_3 = open_modeflags[m]; - fileio_info->param_4 = 0644; - } - } else { - if (l <= 255 && m <= 11) { - if (strcmp((char *)fn, ":tt") == 0) { - if (m < 4) - arm->semihosting_result = dup(STDIN_FILENO); - else - arm->semihosting_result = dup(STDOUT_FILENO); - } else { - /* cygwin requires the permission setting - * otherwise it will fail to reopen a previously - * written file */ - arm->semihosting_result = open((char *)fn, open_modeflags[m], 0644); - } - arm->semihosting_errno = errno; - } else { - arm->semihosting_result = -1; - arm->semihosting_errno = EINVAL; - } - } - } - break; - - case 0x02: /* SYS_CLOSE */ - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "close"; - fileio_info->param_1 = fd; - } else { - arm->semihosting_result = close(fd); - arm->semihosting_errno = errno; - } - } - break; - - case 0x03: /* SYS_WRITEC */ - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "write"; - fileio_info->param_1 = 1; - fileio_info->param_2 = r1; - fileio_info->param_3 = 1; - } else { - unsigned char c; - retval = target_read_memory(target, r1, 1, 1, &c); - if (retval != ERROR_OK) - return retval; - putchar(c); - arm->semihosting_result = 0; - } - break; - - case 0x04: /* SYS_WRITE0 */ - if (arm->is_semihosting_fileio) { - size_t count = 0; - for (uint32_t a = r1;; a++) { - unsigned char c; - retval = target_read_memory(target, a, 1, 1, &c); - if (retval != ERROR_OK) - return retval; - if (c == '\0') - break; - count++; - } - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "write"; - fileio_info->param_1 = 1; - fileio_info->param_2 = r1; - fileio_info->param_3 = count; - } else { - do { - unsigned char c; - retval = target_read_memory(target, r1++, 1, 1, &c); - if (retval != ERROR_OK) - return retval; - if (!c) - break; - putchar(c); - } while (1); - arm->semihosting_result = 0; - } - break; - - case 0x05: /* SYS_WRITE */ - retval = target_read_memory(target, r1, 4, 3, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - uint32_t a = target_buffer_get_u32(target, params+4); - size_t l = target_buffer_get_u32(target, params+8); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "write"; - fileio_info->param_1 = fd; - fileio_info->param_2 = a; - fileio_info->param_3 = l; - } else { - uint8_t *buf = malloc(l); - if (!buf) { - arm->semihosting_result = -1; - arm->semihosting_errno = ENOMEM; - } else { - retval = target_read_buffer(target, a, l, buf); - if (retval != ERROR_OK) { - free(buf); - return retval; - } - arm->semihosting_result = write(fd, buf, l); - arm->semihosting_errno = errno; - if (arm->semihosting_result >= 0) - arm->semihosting_result = l - arm->semihosting_result; - free(buf); - } - } - } - break; - - case 0x06: /* SYS_READ */ - retval = target_read_memory(target, r1, 4, 3, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - uint32_t a = target_buffer_get_u32(target, params+4); - ssize_t l = target_buffer_get_u32(target, params+8); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "read"; - fileio_info->param_1 = fd; - fileio_info->param_2 = a; - fileio_info->param_3 = l; - } else { - uint8_t *buf = malloc(l); - if (!buf) { - arm->semihosting_result = -1; - arm->semihosting_errno = ENOMEM; - } else { - arm->semihosting_result = read(fd, buf, l); - arm->semihosting_errno = errno; - if (arm->semihosting_result >= 0) { - retval = target_write_buffer(target, a, arm->semihosting_result, buf); - if (retval != ERROR_OK) { - free(buf); - return retval; - } - arm->semihosting_result = l - arm->semihosting_result; - } - free(buf); - } - } - } - break; - - case 0x07: /* SYS_READC */ - if (arm->is_semihosting_fileio) { - LOG_ERROR("SYS_READC not supported by semihosting fileio"); - return ERROR_FAIL; - } - arm->semihosting_result = getchar(); - break; - - case 0x08: /* SYS_ISERROR */ - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - arm->semihosting_result = (target_buffer_get_u32(target, params+0) != 0); - break; - - case 0x09: /* SYS_ISTTY */ - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "isatty"; - fileio_info->param_1 = r1; - } else { - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - arm->semihosting_result = isatty(target_buffer_get_u32(target, params+0)); - } - break; - - case 0x0a: /* SYS_SEEK */ - retval = target_read_memory(target, r1, 4, 2, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - off_t pos = target_buffer_get_u32(target, params+4); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "lseek"; - fileio_info->param_1 = fd; - fileio_info->param_2 = pos; - fileio_info->param_3 = SEEK_SET; - } else { - arm->semihosting_result = lseek(fd, pos, SEEK_SET); - arm->semihosting_errno = errno; - if (arm->semihosting_result == pos) - arm->semihosting_result = 0; - } - } - break; - - case 0x0c: /* SYS_FLEN */ - if (arm->is_semihosting_fileio) { - LOG_ERROR("SYS_FLEN not supported by semihosting fileio"); - return ERROR_FAIL; - } - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - else { - int fd = target_buffer_get_u32(target, params+0); - struct stat buf; - arm->semihosting_result = fstat(fd, &buf); - if (arm->semihosting_result == -1) { - arm->semihosting_errno = errno; - arm->semihosting_result = -1; - break; - } - arm->semihosting_result = buf.st_size; - } - break; - - case 0x0e: /* SYS_REMOVE */ - retval = target_read_memory(target, r1, 4, 2, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a = target_buffer_get_u32(target, params+0); - uint32_t l = target_buffer_get_u32(target, params+4); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "unlink"; - fileio_info->param_1 = a; - fileio_info->param_2 = l; - } else { - if (l <= 255) { - uint8_t fn[256]; - retval = target_read_memory(target, a, 1, l, fn); - if (retval != ERROR_OK) - return retval; - fn[l] = 0; - arm->semihosting_result = remove((char *)fn); - arm->semihosting_errno = errno; - } else { - arm->semihosting_result = -1; - arm->semihosting_errno = EINVAL; - } - } - } - break; - - case 0x0f: /* SYS_RENAME */ - retval = target_read_memory(target, r1, 4, 4, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a1 = target_buffer_get_u32(target, params+0); - uint32_t l1 = target_buffer_get_u32(target, params+4); - uint32_t a2 = target_buffer_get_u32(target, params+8); - uint32_t l2 = target_buffer_get_u32(target, params+12); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "rename"; - fileio_info->param_1 = a1; - fileio_info->param_2 = l1; - fileio_info->param_3 = a2; - fileio_info->param_4 = l2; - } else { - if (l1 <= 255 && l2 <= 255) { - uint8_t fn1[256], fn2[256]; - retval = target_read_memory(target, a1, 1, l1, fn1); - if (retval != ERROR_OK) - return retval; - retval = target_read_memory(target, a2, 1, l2, fn2); - if (retval != ERROR_OK) - return retval; - fn1[l1] = 0; - fn2[l2] = 0; - arm->semihosting_result = rename((char *)fn1, (char *)fn2); - arm->semihosting_errno = errno; - } else { - arm->semihosting_result = -1; - arm->semihosting_errno = EINVAL; - } - } - } - break; - - case 0x11: /* SYS_TIME */ - arm->semihosting_result = time(NULL); - break; - - case 0x13: /* SYS_ERRNO */ - arm->semihosting_result = arm->semihosting_errno; - break; - - case 0x15: /* SYS_GET_CMDLINE */ - retval = target_read_memory(target, r1, 4, 2, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a = target_buffer_get_u32(target, params+0); - uint32_t l = target_buffer_get_u32(target, params+4); - char *arg = arm->semihosting_cmdline != NULL ? arm->semihosting_cmdline : ""; - uint32_t s = strlen(arg) + 1; - if (l < s) - arm->semihosting_result = -1; - else { - retval = target_write_buffer(target, a, s, (uint8_t *)arg); - if (retval != ERROR_OK) - return retval; - arm->semihosting_result = 0; - } - } - break; - - case 0x16: /* SYS_HEAPINFO */ - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t a = target_buffer_get_u32(target, params+0); - /* tell the remote we have no idea */ - memset(params, 0, 4*4); - retval = target_write_memory(target, a, 4, 4, params); - if (retval != ERROR_OK) - return retval; - arm->semihosting_result = 0; - } - break; - - case 0x18: /* angel_SWIreason_ReportException */ - switch (r1) { - case 0x20026: /* ADP_Stopped_ApplicationExit */ - fprintf(stderr, "semihosting: *** application exited ***\n"); - break; - case 0x20000: /* ADP_Stopped_BranchThroughZero */ - case 0x20001: /* ADP_Stopped_UndefinedInstr */ - case 0x20002: /* ADP_Stopped_SoftwareInterrupt */ - case 0x20003: /* ADP_Stopped_PrefetchAbort */ - case 0x20004: /* ADP_Stopped_DataAbort */ - case 0x20005: /* ADP_Stopped_AddressException */ - case 0x20006: /* ADP_Stopped_IRQ */ - case 0x20007: /* ADP_Stopped_FIQ */ - case 0x20020: /* ADP_Stopped_BreakPoint */ - case 0x20021: /* ADP_Stopped_WatchPoint */ - case 0x20022: /* ADP_Stopped_StepComplete */ - case 0x20023: /* ADP_Stopped_RunTimeErrorUnknown */ - case 0x20024: /* ADP_Stopped_InternalError */ - case 0x20025: /* ADP_Stopped_UserInterruption */ - case 0x20027: /* ADP_Stopped_StackOverflow */ - case 0x20028: /* ADP_Stopped_DivisionByZero */ - case 0x20029: /* ADP_Stopped_OSSpecific */ - default: - fprintf(stderr, "semihosting: exception %#x\n", - (unsigned) r1); - } - return target_call_event_callbacks(target, TARGET_EVENT_HALTED); - - case 0x12: /* SYS_SYSTEM */ - /* Provide SYS_SYSTEM functionality. Uses the - * libc system command, there may be a reason *NOT* - * to use this, but as I can't think of one, I - * implemented it this way. - */ - retval = target_read_memory(target, r1, 4, 2, params); - if (retval != ERROR_OK) - return retval; - else { - uint32_t len = target_buffer_get_u32(target, params+4); - uint32_t c_ptr = target_buffer_get_u32(target, params); - if (arm->is_semihosting_fileio) { - arm->semihosting_hit_fileio = true; - fileio_info->identifier = "system"; - fileio_info->param_1 = c_ptr; - fileio_info->param_2 = len; - } else { - uint8_t cmd[256]; - if (len > 255) { - arm->semihosting_result = -1; - arm->semihosting_errno = EINVAL; - } else { - memset(cmd, 0x0, 256); - retval = target_read_memory(target, c_ptr, 1, len, cmd); - if (retval != ERROR_OK) - return retval; - else - arm->semihosting_result = system((const char *)cmd); - } - } - } - break; - case 0x0d: /* SYS_TMPNAM */ - case 0x10: /* SYS_CLOCK */ - case 0x17: /* angel_SWIreason_EnterSVC */ - case 0x30: /* SYS_ELAPSED */ - case 0x31: /* SYS_TICKFREQ */ - default: - fprintf(stderr, "semihosting: unsupported call %#x\n", - (unsigned) r0); - arm->semihosting_result = -1; - arm->semihosting_errno = ENOTSUP; - } - - return ERROR_OK; -} - -static int get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info) -{ - struct arm *arm = target_to_arm(target); - - /* To avoid uneccessary duplication, semihosting prepares the - * fileio_info structure out-of-band when the target halts. See - * do_semihosting for more detail. - */ - if (!arm->is_semihosting_fileio || !arm->semihosting_hit_fileio) - return ERROR_FAIL; - - return ERROR_OK; -} - -static int gdb_fileio_end(struct target *target, int result, int fileio_errno, bool ctrl_c) -{ - struct arm *arm = target_to_arm(target); - struct gdb_fileio_info *fileio_info = target->fileio_info; - - /* clear pending status */ - arm->semihosting_hit_fileio = false; - - arm->semihosting_result = result; - arm->semihosting_errno = fileio_errno; - - /* Some fileio results do not match up with what the semihosting - * operation expects; for these operations, we munge the results - * below: - */ - switch (arm->semihosting_op) { - case 0x05: /* SYS_WRITE */ - if (result < 0) - arm->semihosting_result = fileio_info->param_3; - else - arm->semihosting_result = 0; - break; - - case 0x06: /* SYS_READ */ - if (result == (int)fileio_info->param_3) - arm->semihosting_result = 0; - if (result <= 0) - arm->semihosting_result = fileio_info->param_3; - break; - - case 0x0a: /* SYS_SEEK */ - if (result > 0) - arm->semihosting_result = 0; - break; - } - - return post_result(target); -} - /** * Initialize ARM semihosting support. * @@ -630,14 +108,9 @@ static int gdb_fileio_end(struct target *target, int result, int fileio_errno, b */ int arm_semihosting_init(struct target *target) { - target->fileio_info = malloc(sizeof(*target->fileio_info)); - if (target->fileio_info == NULL) { - LOG_ERROR("out of memory"); - return ERROR_FAIL; - } - - target->type->get_gdb_fileio_info = get_gdb_fileio_info; - target->type->gdb_fileio_end = gdb_fileio_end; + struct arm *arm = target_to_arm(target); + assert(arm->setup_semihosting); + semihosting_common_init(target, arm->setup_semihosting, post_result); return ERROR_OK; } @@ -662,7 +135,11 @@ int arm_semihosting(struct target *target, int *retval) uint32_t pc, lr, spsr; struct reg *r; - if (!arm->is_semihosting) + struct semihosting *semihosting = target->semihosting; + if (!semihosting) + return 0; + + if (!semihosting->is_active) return 0; if (is_arm7_9(target_to_arm7_9(target)) || @@ -766,10 +243,24 @@ int arm_semihosting(struct target *target, int *retval) /* Perform semihosting if we are not waiting on a fileio * operation to complete. */ - if (!arm->semihosting_hit_fileio) { - *retval = do_semihosting(target); - if (*retval != ERROR_OK) { - LOG_ERROR("Failed semihosting operation"); + if (!semihosting->hit_fileio) { + /* TODO: update for 64-bits */ + uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32); + uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32); + + semihosting->op = r0; + semihosting->param = r1; + semihosting->word_size_bytes = 4; + + /* Check for ARM operation numbers. */ + if (0 <= semihosting->op && semihosting->op <= 0x31) { + *retval = semihosting_common(target); + if (*retval != ERROR_OK) { + LOG_ERROR("Failed semihosting operation"); + return 0; + } + } else { + /* Unknown operation number, not a semihosting call. */ return 0; } } @@ -777,13 +268,8 @@ int arm_semihosting(struct target *target, int *retval) /* Post result to target if we are not waiting on a fileio * operation to complete: */ - if (!arm->semihosting_hit_fileio) { - *retval = post_result(target); - if (*retval != ERROR_OK) { - LOG_ERROR("Failed to post semihosting result"); - return 0; - } - + if (semihosting->is_resumable && !semihosting->hit_fileio) { + /* Resume right after the BRK instruction. */ *retval = target_resume(target, 1, 0, 0, 0); if (*retval != ERROR_OK) { LOG_ERROR("Failed to resume target"); diff --git a/src/target/arm_semihosting.h b/src/target/arm_semihosting.h index 011f19f00..cf1f8de14 100644 --- a/src/target/arm_semihosting.h +++ b/src/target/arm_semihosting.h @@ -19,6 +19,8 @@ #ifndef OPENOCD_TARGET_ARM_SEMIHOSTING_H #define OPENOCD_TARGET_ARM_SEMIHOSTING_H +#include "semihosting_common.h" + int arm_semihosting_init(struct target *target); int arm_semihosting(struct target *target, int *retval); diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c index 06994ca7b..5ee8ead66 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -8,6 +8,9 @@ * Copyright (C) 2008 by Oyvind Harboe * * oyvind.harboe@zylin.com * * * + * Copyright (C) 2018 by Liviu Ionescu * + * * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -34,6 +37,7 @@ #include #include "algorithm.h" #include "register.h" +#include "semihosting_common.h" /* offsets into armv4_5 core register cache */ enum { @@ -748,7 +752,7 @@ int arm_arch_state(struct target *target) } /* avoid filling log waiting for fileio reply */ - if (arm->semihosting_hit_fileio) + if (target->semihosting->hit_fileio) return ERROR_OK; LOG_USER("target halted in %s state due to %s, current mode: %s\n" @@ -758,8 +762,8 @@ int arm_arch_state(struct target *target) arm_mode_name(arm->core_mode), buf_get_u32(arm->cpsr->value, 0, 32), buf_get_u32(arm->pc->value, 0, 32), - arm->is_semihosting ? ", semihosting" : "", - arm->is_semihosting_fileio ? " fileio" : ""); + target->semihosting->is_active ? ", semihosting" : "", + target->semihosting->is_fileio ? " fileio" : ""); return ERROR_OK; } @@ -1094,119 +1098,10 @@ static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv) return JIM_OK; } -COMMAND_HANDLER(handle_arm_semihosting_command) -{ - struct target *target = get_current_target(CMD_CTX); - - if (target == NULL) { - LOG_ERROR("No target selected"); - return ERROR_FAIL; - } - - struct arm *arm = target_to_arm(target); - - if (!is_arm(arm)) { - command_print(CMD_CTX, "current target isn't an ARM"); - return ERROR_FAIL; - } - - if (!arm->setup_semihosting) { - command_print(CMD_CTX, "semihosting not supported for current target"); - return ERROR_FAIL; - } - - if (CMD_ARGC > 0) { - int semihosting; - - COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting); - - if (!target_was_examined(target)) { - LOG_ERROR("Target not examined yet"); - return ERROR_FAIL; - } - - if (arm->setup_semihosting(target, semihosting) != ERROR_OK) { - LOG_ERROR("Failed to Configure semihosting"); - return ERROR_FAIL; - } - - /* FIXME never let that "catch" be dropped! */ - arm->is_semihosting = semihosting; - } - - command_print(CMD_CTX, "semihosting is %s", - arm->is_semihosting - ? "enabled" : "disabled"); - - return ERROR_OK; -} - -COMMAND_HANDLER(handle_arm_semihosting_fileio_command) -{ - struct target *target = get_current_target(CMD_CTX); - - if (target == NULL) { - LOG_ERROR("No target selected"); - return ERROR_FAIL; - } - - struct arm *arm = target_to_arm(target); - - if (!is_arm(arm)) { - command_print(CMD_CTX, "current target isn't an ARM"); - return ERROR_FAIL; - } - - if (!arm->is_semihosting) { - command_print(CMD_CTX, "semihosting is not enabled"); - return ERROR_FAIL; - } - - if (CMD_ARGC > 0) - COMMAND_PARSE_ENABLE(CMD_ARGV[0], arm->is_semihosting_fileio); - - command_print(CMD_CTX, "semihosting fileio is %s", - arm->is_semihosting_fileio - ? "enabled" : "disabled"); - - return ERROR_OK; -} - -COMMAND_HANDLER(handle_arm_semihosting_cmdline) -{ - struct target *target = get_current_target(CMD_CTX); - unsigned int i; - - if (target == NULL) { - LOG_ERROR("No target selected"); - return ERROR_FAIL; - } - - struct arm *arm = target_to_arm(target); - - if (!is_arm(arm)) { - command_print(CMD_CTX, "current target isn't an ARM"); - return ERROR_FAIL; - } - - if (!arm->setup_semihosting) { - command_print(CMD_CTX, "semihosting not supported for current target"); - return ERROR_FAIL; - } - - free(arm->semihosting_cmdline); - arm->semihosting_cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL; - - for (i = 1; i < CMD_ARGC; i++) { - char *cmdline = alloc_printf("%s %s", arm->semihosting_cmdline, CMD_ARGV[i]); - if (cmdline == NULL) - break; - free(arm->semihosting_cmdline); - arm->semihosting_cmdline = cmdline; - } - - return ERROR_OK; -} +extern __COMMAND_HANDLER(handle_common_semihosting_command); +extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command); +extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command); +extern __COMMAND_HANDLER(handle_common_semihosting_cmdline); static const struct command_registration arm_exec_command_handlers[] = { { @@ -1245,26 +1140,32 @@ static const struct command_registration arm_exec_command_handlers[] = { }, { "semihosting", - .handler = handle_arm_semihosting_command, + .handler = handle_common_semihosting_command, .mode = COMMAND_EXEC, .usage = "['enable'|'disable']", .help = "activate support for semihosting operations", }, { "semihosting_cmdline", - .handler = handle_arm_semihosting_cmdline, + .handler = handle_common_semihosting_cmdline, .mode = COMMAND_EXEC, .usage = "arguments", .help = "command line arguments to be passed to program", }, { "semihosting_fileio", - .handler = handle_arm_semihosting_fileio_command, + .handler = handle_common_semihosting_fileio_command, .mode = COMMAND_EXEC, .usage = "['enable'|'disable']", .help = "activate support for semihosting fileio operations", }, - + { + "semihosting_resexit", + .handler = handle_common_semihosting_resumable_exit_command, + .mode = COMMAND_EXEC, + .usage = "['enable'|'disable']", + .help = "activate support for semihosting resumable exit", + }, COMMAND_REGISTRATION_DONE }; const struct command_registration arm_command_handlers[] = { diff --git a/src/target/armv7m.c b/src/target/armv7m.c index 7b7893f64..1b4e5b154 100644 --- a/src/target/armv7m.c +++ b/src/target/armv7m.c @@ -11,6 +11,9 @@ * Copyright (C) 2007,2008 Øyvind Harboe * * oyvind.harboe@zylin.com * * * + * Copyright (C) 2018 by Liviu Ionescu * + * * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -37,6 +40,7 @@ #include "armv7m.h" #include "algorithm.h" #include "register.h" +#include "semihosting_common.h" #if 0 #define _DEBUG_INSTRUCTION_EXECUTION_ @@ -537,7 +541,7 @@ int armv7m_arch_state(struct target *target) uint32_t ctrl, sp; /* avoid filling log waiting for fileio reply */ - if (arm->semihosting_hit_fileio) + if (target->semihosting->hit_fileio) return ERROR_OK; ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32); @@ -552,8 +556,8 @@ int armv7m_arch_state(struct target *target) buf_get_u32(arm->pc->value, 0, 32), (ctrl & 0x02) ? 'p' : 'm', sp, - arm->is_semihosting ? ", semihosting" : "", - arm->is_semihosting_fileio ? " fileio" : ""); + target->semihosting->is_active ? ", semihosting" : "", + target->semihosting->is_fileio ? " fileio" : ""); return ERROR_OK; } diff --git a/src/target/armv8.c b/src/target/armv8.c index 82b2b2495..20f2b671c 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -1,6 +1,9 @@ /*************************************************************************** * Copyright (C) 2015 by David Ung * * * + * Copyright (C) 2018 by Liviu Ionescu * + * * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -36,6 +39,7 @@ #include "armv8_opcodes.h" #include "target.h" #include "target_type.h" +#include "semihosting_common.h" static const char * const armv8_state_strings[] = { "AArch32", "Thumb", "Jazelle", "ThumbEE", "AArch64", @@ -1046,7 +1050,7 @@ int armv8_aarch64_state(struct target *target) armv8_mode_name(arm->core_mode), buf_get_u32(arm->cpsr->value, 0, 32), buf_get_u64(arm->pc->value, 0, 64), - arm->is_semihosting ? ", semihosting" : ""); + target->semihosting->is_active ? ", semihosting" : ""); return ERROR_OK; } diff --git a/src/target/nds32.c b/src/target/nds32.c index e4bb17f9d..4115ea4f2 100644 --- a/src/target/nds32.c +++ b/src/target/nds32.c @@ -2339,63 +2339,66 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil fileio_info->identifier = NULL; } + uint32_t reg_r0, reg_r1, reg_r2; + nds32_get_mapped_reg(nds32, R0, ®_r0); + nds32_get_mapped_reg(nds32, R1, ®_r1); + nds32_get_mapped_reg(nds32, R2, ®_r2); + switch (syscall_id) { case NDS32_SYSCALL_EXIT: fileio_info->identifier = malloc(5); sprintf(fileio_info->identifier, "exit"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; break; case NDS32_SYSCALL_OPEN: { uint8_t filename[256]; fileio_info->identifier = malloc(5); sprintf(fileio_info->identifier, "open"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* 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)); + fileio_info->param_3 = reg_r1; + fileio_info->param_4 = reg_r2; - target->type->read_buffer(target, fileio_info->param_1, - 256, filename); + target->type->read_buffer(target, reg_r0, 256, filename); fileio_info->param_2 = strlen((char *)filename) + 1; } break; case NDS32_SYSCALL_CLOSE: fileio_info->identifier = malloc(6); sprintf(fileio_info->identifier, "close"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; break; case NDS32_SYSCALL_READ: fileio_info->identifier = 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)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; + fileio_info->param_3 = reg_r2; break; case NDS32_SYSCALL_WRITE: fileio_info->identifier = 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)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; + fileio_info->param_3 = reg_r2; break; case NDS32_SYSCALL_LSEEK: fileio_info->identifier = 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)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; + fileio_info->param_3 = reg_r2; break; case NDS32_SYSCALL_UNLINK: { uint8_t filename[256]; fileio_info->identifier = malloc(7); sprintf(fileio_info->identifier, "unlink"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* reserve fileio_info->param_2 for length of path */ - target->type->read_buffer(target, fileio_info->param_1, - 256, filename); + target->type->read_buffer(target, reg_r0, 256, filename); fileio_info->param_2 = strlen((char *)filename) + 1; } break; @@ -2404,61 +2407,57 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil uint8_t filename[256]; fileio_info->identifier = malloc(7); sprintf(fileio_info->identifier, "rename"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* reserve fileio_info->param_2 for length of old path */ - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3)); + fileio_info->param_3 = reg_r1; /* reserve fileio_info->param_4 for length of new path */ - target->type->read_buffer(target, fileio_info->param_1, - 256, filename); + target->type->read_buffer(target, reg_r0, 256, filename); fileio_info->param_2 = strlen((char *)filename) + 1; - target->type->read_buffer(target, fileio_info->param_3, - 256, filename); + target->type->read_buffer(target, reg_r1, 256, filename); fileio_info->param_4 = strlen((char *)filename) + 1; } break; case NDS32_SYSCALL_FSTAT: fileio_info->identifier = 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)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; break; case NDS32_SYSCALL_STAT: { uint8_t filename[256]; fileio_info->identifier = malloc(5); sprintf(fileio_info->identifier, "stat"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* reserve fileio_info->param_2 for length of old path */ - nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3)); + fileio_info->param_3 = reg_r1; - target->type->read_buffer(target, fileio_info->param_1, - 256, filename); + target->type->read_buffer(target, reg_r0, 256, filename); fileio_info->param_2 = strlen((char *)filename) + 1; } break; case NDS32_SYSCALL_GETTIMEOFDAY: fileio_info->identifier = 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)); + fileio_info->param_1 = reg_r0; + fileio_info->param_2 = reg_r1; break; case NDS32_SYSCALL_ISATTY: fileio_info->identifier = malloc(7); sprintf(fileio_info->identifier, "isatty"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; break; case NDS32_SYSCALL_SYSTEM: { uint8_t command[256]; fileio_info->identifier = malloc(7); sprintf(fileio_info->identifier, "system"); - nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1)); + fileio_info->param_1 = reg_r0; /* reserve fileio_info->param_2 for length of old path */ - target->type->read_buffer(target, fileio_info->param_1, - 256, command); + target->type->read_buffer(target, reg_r0, 256, command); fileio_info->param_2 = strlen((char *)command) + 1; } break; diff --git a/src/target/semihosting_common.c b/src/target/semihosting_common.c new file mode 100644 index 000000000..beeb4742a --- /dev/null +++ b/src/target/semihosting_common.c @@ -0,0 +1,1579 @@ +/*************************************************************************** + * Copyright (C) 2018 by Liviu Ionescu * + * * + * * + * Copyright (C) 2018 by Marvell Technology Group Ltd. * + * Written by Nicolas Pitre * + * * + * Copyright (C) 2010 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * Copyright (C) 2016 by Square, Inc. * + * Steven Stallion * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +/** + * @file + * Common ARM semihosting support. + * + * Semihosting enables code running on a target to use some of the I/O + * facilities on the host computer. The target application must be linked + * against a library that forwards operation requests by using an + * instruction trapped by the debugger. + * + * Details can be found in + * "Semihosting for AArch32 and AArch64, Release 2.0" + * https://static.docs.arm.com/100863/0200/semihosting.pdf + * from ARM Ltd. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "target.h" +#include "target_type.h" +#include "semihosting_common.h" + +#include +#include +#include + +static const int open_modeflags[12] = { + O_RDONLY, + O_RDONLY | O_BINARY, + O_RDWR, + O_RDWR | O_BINARY, + O_WRONLY | O_CREAT | O_TRUNC, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + O_RDWR | O_CREAT | O_TRUNC, + O_RDWR | O_CREAT | O_TRUNC | O_BINARY, + O_WRONLY | O_CREAT | O_APPEND, + O_WRONLY | O_CREAT | O_APPEND | O_BINARY, + O_RDWR | O_CREAT | O_APPEND, + O_RDWR | O_CREAT | O_APPEND | O_BINARY +}; + +static int semihosting_common_fileio_info(struct target *target, + struct gdb_fileio_info *fileio_info); +static int semihosting_common_fileio_end(struct target *target, int result, + int fileio_errno, bool ctrl_c); + +static int semihosting_read_fields(struct target *target, size_t number, + uint8_t *fields); +static int semihosting_write_fields(struct target *target, size_t number, + uint8_t *fields); +static uint64_t semihosting_get_field(struct target *target, size_t index, + uint8_t *fields); +static void semihosting_set_field(struct target *target, uint64_t value, + size_t index, + uint8_t *fields); + +/* Attempts to include gdb_server.h failed. */ +extern int gdb_actual_connections; + +/** + * Initialize common semihosting support. + * + * @param target Pointer to the target to initialize. + * @return An error status if there is a problem during initialization. + */ +int semihosting_common_init(struct target *target, void *setup, + void *post_result) +{ + LOG_DEBUG(" "); + + target->fileio_info = malloc(sizeof(*target->fileio_info)); + if (target->fileio_info == NULL) { + LOG_ERROR("out of memory"); + return ERROR_FAIL; + } + memset(target->fileio_info, 0, sizeof(*target->fileio_info)); + + struct semihosting *semihosting; + semihosting = malloc(sizeof(*target->semihosting)); + if (semihosting == NULL) { + LOG_ERROR("out of memory"); + return ERROR_FAIL; + } + + semihosting->is_active = false; + semihosting->is_fileio = false; + semihosting->hit_fileio = false; + semihosting->is_resumable = false; + semihosting->has_resumable_exit = false; + semihosting->word_size_bytes = 0; + semihosting->op = -1; + semihosting->param = 0; + semihosting->result = -1; + semihosting->sys_errno = -1; + semihosting->cmdline = NULL; + + /* If possible, update it in setup(). */ + semihosting->setup_time = clock(); + + semihosting->setup = setup; + semihosting->post_result = post_result; + + target->semihosting = semihosting; + + target->type->get_gdb_fileio_info = semihosting_common_fileio_info; + target->type->gdb_fileio_end = semihosting_common_fileio_end; + + return ERROR_OK; +} + +/** + * Portable implementation of ARM semihosting calls. + * Performs the currently pending semihosting operation + * encoded in target->semihosting. + */ +int semihosting_common(struct target *target) +{ + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + /* Silently ignore if the semhosting field was not set. */ + return ERROR_OK; + } + + struct gdb_fileio_info *fileio_info = target->fileio_info; + + /* + * By default return an error. + * The actual result must be set by each function + */ + semihosting->result = -1; + + /* Most operations are resumable, except the two exit calls. */ + semihosting->is_resumable = true; + + int retval; + + /* Enough space to hold 4 long words. */ + uint8_t fields[4*8]; + + LOG_DEBUG("op=0x%x, param=0x%" PRIx64, (int)semihosting->op, + semihosting->param); + + switch (semihosting->op) { + + case SEMIHOSTING_SYS_CLOCK: /* 0x10 */ + /* + * Returns the number of centiseconds (hundredths of a second) + * since the execution started. + * + * Values returned can be of limited use for some benchmarking + * purposes because of communication overhead or other + * agent-specific factors. For example, with a debug hardware + * unit the request is passed back to the host for execution. + * This can lead to unpredictable delays in transmission and + * process scheduling. + * + * Use this function to calculate time intervals, by calculating + * differences between intervals with and without the code + * sequence to be timed. + * + * Entry + * The PARAMETER REGISTER must contain 0. There are no other + * parameters. + * + * Return + * On exit, the RETURN REGISTER contains: + * - The number of centiseconds since some arbitrary start + * point, if the call is successful. + * - –1 if the call is not successful. For example, because + * of a communications error. + */ + { + clock_t delta = clock() - semihosting->setup_time; + + semihosting->result = delta / (CLOCKS_PER_SEC / 100); + } + break; + + case SEMIHOSTING_SYS_CLOSE: /* 0x02 */ + /* + * Closes a file on the host system. The handle must reference + * a file that was opened with SYS_OPEN. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * one-field argument block: + * - field 1 Contains a handle for an open file. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the call is successful + * - –1 if the call is not successful. + */ + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "close"; + fileio_info->param_1 = fd; + } else { + semihosting->result = close(fd); + semihosting->sys_errno = errno; + + LOG_DEBUG("close(%d)=%d", fd, (int)semihosting->result); + } + } + break; + + case SEMIHOSTING_SYS_ERRNO: /* 0x13 */ + /* + * Returns the value of the C library errno variable that is + * associated with the semihosting implementation. The errno + * variable can be set by a number of C library semihosted + * functions, including: + * - SYS_REMOVE + * - SYS_OPEN + * - SYS_CLOSE + * - SYS_READ + * - SYS_WRITE + * - SYS_SEEK. + * + * Whether errno is set or not, and to what value, is entirely + * host-specific, except where the ISO C standard defines the + * behavior. + * + * Entry + * There are no parameters. The PARAMETER REGISTER must be 0. + * + * Return + * On exit, the RETURN REGISTER contains the value of the C + * library errno variable. + */ + semihosting->result = semihosting->sys_errno; + break; + + case SEMIHOSTING_SYS_EXIT: /* 0x18 */ + /* + * Note: SYS_EXIT was called angel_SWIreason_ReportException in + * previous versions of the documentation. + * + * An application calls this operation to report an exception + * to the debugger directly. The most common use is to report + * that execution has completed, using ADP_Stopped_ApplicationExit. + * + * Note: This semihosting operation provides no means for 32-bit + * callers to indicate an application exit with a specified exit + * code. Semihosting callers may prefer to check for the presence + * of the SH_EXT_EXTENDED_REPORT_EXCEPTION extension and use + * the SYS_REPORT_EXCEPTION_EXTENDED operation instead, if it + * is available. + * + * Entry (32-bit) + * On entry, the PARAMETER register is set to a reason code + * describing the cause of the trap. Not all semihosting client + * implementations will necessarily trap every corresponding + * event. Important reason codes are: + * + * - ADP_Stopped_ApplicationExit 0x20026 + * - ADP_Stopped_RunTimeErrorUnknown 0x20023 + * + * Entry (64-bit) + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field argument block: + * - field 1 The exception type, which is one of the set of + * reason codes in the above tables. + * - field 2 A subcode, whose meaning depends on the reason + * code in field 1. + * In particular, if field 1 is ADP_Stopped_ApplicationExit + * then field 2 is an exit status code, as passed to the C + * standard library exit() function. A simulator receiving + * this request must notify a connected debugger, if present, + * and then exit with the specified status. + * + * Return + * No return is expected from these calls. However, it is + * possible for the debugger to request that the application + * continues by performing an RDI_Execute request or equivalent. + * In this case, execution continues with the registers as they + * were on entry to the operation, or as subsequently modified + * by the debugger. + */ + if (semihosting->word_size_bytes == 8) { + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + int type = semihosting_get_field(target, 0, fields); + int code = semihosting_get_field(target, 1, fields); + + if (type == ADP_STOPPED_APPLICATION_EXIT) { + if (!gdb_actual_connections) + exit(code); + else { + fprintf(stderr, + "semihosting: *** application exited with %d ***\n", + code); + } + } else { + fprintf(stderr, + "semihosting: application exception %#x\n", + type); + } + } + } else { + if (semihosting->param == ADP_STOPPED_APPLICATION_EXIT) { + if (!gdb_actual_connections) + exit(0); + else { + fprintf(stderr, + "semihosting: *** application exited normally ***\n"); + } + } else if (semihosting->param == ADP_STOPPED_RUN_TIME_ERROR) { + /* Chosen more or less arbitrarly to have a nicer message, + * otherwise all other return the same exit code 1. */ + if (!gdb_actual_connections) + exit(1); + else { + fprintf(stderr, + "semihosting: *** application exited with error ***\n"); + } + } else { + if (!gdb_actual_connections) + exit(1); + else { + fprintf(stderr, + "semihosting: application exception %#x\n", + (unsigned) semihosting->param); + } + } + } + if (!semihosting->has_resumable_exit) { + semihosting->is_resumable = false; + return target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + break; + + case SEMIHOSTING_SYS_EXIT_EXTENDED: /* 0x20 */ + /* + * This operation is only supported if the semihosting extension + * SH_EXT_EXIT_EXTENDED is implemented. SH_EXT_EXIT_EXTENDED is + * reported using feature byte 0, bit 0. If this extension is + * supported, then the implementation provides a means to + * report a normal exit with a nonzero exit status in both 32-bit + * and 64-bit semihosting APIs. + * + * The implementation must provide the semihosting call + * SYS_EXIT_EXTENDED for both A64 and A32/T32 semihosting APIs. + * + * SYS_EXIT_EXTENDED is used by an application to report an + * exception or exit to the debugger directly. The most common + * use is to report that execution has completed, using + * ADP_Stopped_ApplicationExit. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field argument block: + * - field 1 The exception type, which should be one of the set + * of reason codes that are documented for the SYS_EXIT + * (0x18) call. For example, ADP_Stopped_ApplicationExit. + * - field 2 A subcode, whose meaning depends on the reason + * code in field 1. In particular, if field 1 is + * ADP_Stopped_ApplicationExit then field 2 is an exit status + * code, as passed to the C standard library exit() function. + * A simulator receiving this request must notify a connected + * debugger, if present, and then exit with the specified status. + * + * Return + * No return is expected from these calls. + * + * For the A64 API, this call is identical to the behavior of + * the mandatory SYS_EXIT (0x18) call. If this extension is + * supported, then both calls must be implemented. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + int type = semihosting_get_field(target, 0, fields); + int code = semihosting_get_field(target, 1, fields); + + if (type == ADP_STOPPED_APPLICATION_EXIT) { + if (!gdb_actual_connections) + exit(code); + else { + fprintf(stderr, + "semihosting: *** application exited with %d ***\n", + code); + } + } else { + fprintf(stderr, "semihosting: exception %#x\n", + type); + } + } + if (!semihosting->has_resumable_exit) { + semihosting->is_resumable = false; + return target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + break; + + case SEMIHOSTING_SYS_FLEN: /* 0x0C */ + /* + * Returns the length of a specified file. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * one-field argument block: + * - field 1 A handle for a previously opened, seekable file + * object. + * + * Return + * On exit, the RETURN REGISTER contains: + * - The current length of the file object, if the call is + * successful. + * - –1 if an error occurs. + */ + if (semihosting->is_fileio) { + LOG_ERROR("SYS_FLEN not supported by semihosting fileio"); + return ERROR_FAIL; + } + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + struct stat buf; + semihosting->result = fstat(fd, &buf); + if (semihosting->result == -1) { + semihosting->sys_errno = errno; + LOG_DEBUG("fstat(%d)=%d", fd, (int)semihosting->result); + break; + } + LOG_DEBUG("fstat(%d)=%d", fd, (int)semihosting->result); + semihosting->result = buf.st_size; + } + break; + + case SEMIHOSTING_SYS_GET_CMDLINE: /* 0x15 */ + /* + * Returns the command line that is used for the call to the + * executable, that is, argc and argv. + * + * Entry + * On entry, the PARAMETER REGISTER points to a two-field data + * block to be used for returning the command string and its length: + * - field 1 A pointer to a buffer of at least the size that is + * specified in field 2. + * - field 2 The length of the buffer in bytes. + * + * Return + * On exit: + * If the call is successful, then the RETURN REGISTER contains 0, + * the PARAMETER REGISTER is unchanged, and the data block is + * updated as follows: + * - field 1 A pointer to a null-terminated string of the command + * line. + * - field 2 The length of the string in bytes. + * If the call is not successful, then the RETURN REGISTER + * contains -1. + * + * Note: The semihosting implementation might impose limits on + * the maximum length of the string that can be transferred. + * However, the implementation must be able to support a + * command-line length of at least 80 bytes. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + size_t size = semihosting_get_field(target, 1, fields); + + char *arg = semihosting->cmdline != NULL ? + semihosting->cmdline : ""; + uint32_t len = strlen(arg) + 1; + if (len > size) + semihosting->result = -1; + else { + semihosting_set_field(target, len, 1, fields); + retval = target_write_buffer(target, addr, len, + (uint8_t *)arg); + if (retval != ERROR_OK) + return retval; + semihosting->result = 0; + + retval = semihosting_write_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + } + LOG_DEBUG("SYS_GET_CMDLINE=[%s],%d", arg, + (int)semihosting->result); + } + break; + + case SEMIHOSTING_SYS_HEAPINFO: /* 0x16 */ + /* + * Returns the system stack and heap parameters. + * + * Entry + * On entry, the PARAMETER REGISTER contains the address of a + * pointer to a four-field data block. The contents of the data + * block are filled by the function. The following C-like + * pseudocode describes the layout of the block: + * struct block { + * void* heap_base; + * void* heap_limit; + * void* stack_base; + * void* stack_limit; + * }; + * + * Return + * On exit, the PARAMETER REGISTER is unchanged and the data + * block has been updated. + */ + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + /* tell the remote we have no idea */ + memset(fields, 0, 4 * semihosting->word_size_bytes); + retval = target_write_memory(target, addr, 4, + semihosting->word_size_bytes, + fields); + if (retval != ERROR_OK) + return retval; + semihosting->result = 0; + } + break; + + case SEMIHOSTING_SYS_ISERROR: /* 0x08 */ + /* + * Determines whether the return code from another semihosting + * call is an error status or not. + * + * This call is passed a parameter block containing the error + * code to examine. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * one-field data block: + * - field 1 The required status word to check. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the status field is not an error indication + * - A nonzero value if the status field is an error indication. + */ + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + + uint64_t code = semihosting_get_field(target, 0, fields); + semihosting->result = (code != 0); + break; + + case SEMIHOSTING_SYS_ISTTY: /* 0x09 */ + /* + * Checks whether a file is connected to an interactive device. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * one-field argument block: + * field 1 A handle for a previously opened file object. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 1 if the handle identifies an interactive device. + * - 0 if the handle identifies a file. + * - A value other than 1 or 0 if an error occurs. + */ + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "isatty"; + fileio_info->param_1 = semihosting->param; + } else { + retval = semihosting_read_fields(target, 1, fields); + if (retval != ERROR_OK) + return retval; + int fd = semihosting_get_field(target, 0, fields); + semihosting->result = isatty(fd); + LOG_DEBUG("isatty(%d)=%d", fd, (int)semihosting->result); + } + break; + + case SEMIHOSTING_SYS_OPEN: /* 0x01 */ + /* + * Opens a file on the host system. + * + * The file path is specified either as relative to the current + * directory of the host process, or absolute, using the path + * conventions of the host operating system. + * + * Semihosting implementations must support opening the special + * path name :semihosting-features as part of the semihosting + * extensions reporting mechanism. + * + * ARM targets interpret the special path name :tt as meaning + * the console input stream, for an open-read or the console + * output stream, for an open-write. Opening these streams is + * performed as part of the standard startup code for those + * applications that reference the C stdio streams. The + * semihosting extension SH_EXT_STDOUT_STDERR allows the + * semihosting caller to open separate output streams + * corresponding to stdout and stderr. This extension is + * reported using feature byte 0, bit 1. Use SYS_OPEN with + * the special path name :semihosting-features to access the + * feature bits. + * + * If this extension is supported, the implementation must + * support the following additional semantics to SYS_OPEN: + * - If the special path name :tt is opened with an fopen + * mode requesting write access (w, wb, w+, or w+b), then + * this is a request to open stdout. + * - If the special path name :tt is opened with a mode + * requesting append access (a, ab, a+, or a+b), then this is + * a request to open stderr. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * three-field argument block: + * - field 1 A pointer to a null-terminated string containing + * a file or device name. + * - field 2 An integer that specifies the file opening mode. + * - field 3 An integer that gives the length of the string + * pointed to by field 1. + * + * The length does not include the terminating null character + * that must be present. + * + * Return + * On exit, the RETURN REGISTER contains: + * - A nonzero handle if the call is successful. + * - –1 if the call is not successful. + */ + retval = semihosting_read_fields(target, 3, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + uint32_t mode = semihosting_get_field(target, 1, fields); + size_t len = semihosting_get_field(target, 2, fields); + + if (mode > 11) { + semihosting->result = -1; + semihosting->sys_errno = EINVAL; + break; + } + uint8_t *fn = malloc(len+1); + if (!fn) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = target_read_memory(target, addr, 1, len, fn); + if (retval != ERROR_OK) { + free(fn); + return retval; + } + fn[len] = 0; + /* TODO: implement the :semihosting-features special file. + * */ + if (semihosting->is_fileio) { + if (strcmp((char *)fn, ":tt") == 0) + semihosting->result = 0; + else { + semihosting->hit_fileio = true; + fileio_info->identifier = "open"; + fileio_info->param_1 = addr; + fileio_info->param_2 = len; + fileio_info->param_3 = open_modeflags[mode]; + fileio_info->param_4 = 0644; + } + } else { + if (strcmp((char *)fn, ":tt") == 0) { + /* Mode is: + * - 0-3 ("r") for stdin, + * - 4-7 ("w") for stdout, + * - 8-11 ("a") for stderr */ + if (mode < 4) { + semihosting->result = dup( + STDIN_FILENO); + semihosting->sys_errno = errno; + LOG_DEBUG("dup(STDIN)=%d", + (int)semihosting->result); + } else if (mode < 8) { + semihosting->result = dup( + STDOUT_FILENO); + semihosting->sys_errno = errno; + LOG_DEBUG("dup(STDOUT)=%d", + (int)semihosting->result); + } else { + semihosting->result = dup( + STDERR_FILENO); + semihosting->sys_errno = errno; + LOG_DEBUG("dup(STDERR)=%d", + (int)semihosting->result); + } + } else { + /* cygwin requires the permission setting + * otherwise it will fail to reopen a previously + * written file */ + semihosting->result = open((char *)fn, + open_modeflags[mode], + 0644); + semihosting->sys_errno = errno; + LOG_DEBUG("open('%s')=%d", fn, + (int)semihosting->result); + } + } + free(fn); + } + } + break; + + case SEMIHOSTING_SYS_READ: /* 0x06 */ + /* + * Reads the contents of a file into a buffer. The file position + * is specified either: + * - Explicitly by a SYS_SEEK. + * - Implicitly one byte beyond the previous SYS_READ or + * SYS_WRITE request. + * + * The file position is at the start of the file when it is + * opened, and is lost when the file is closed. Perform the + * file operation as a single action whenever possible. For + * example, do not split a read of 16KB into four 4KB chunks + * unless there is no alternative. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * three-field data block: + * - field 1 Contains a handle for a file previously opened + * with SYS_OPEN. + * - field 2 Points to a buffer. + * - field 3 Contains the number of bytes to read to the buffer + * from the file. + * + * Return + * On exit, the RETURN REGISTER contains the number of bytes not + * filled in the buffer (buffer_length - bytes_read) as follows: + * - If the RETURN REGISTER is 0, the entire buffer was + * successfully filled. + * - If the RETURN REGISTER is the same as field 3, no bytes + * were read (EOF can be assumed). + * - If the RETURN REGISTER contains a value smaller than + * field 3, the read succeeded but the buffer was only partly + * filled. For interactive devices, this is the most common + * return value. + */ + retval = semihosting_read_fields(target, 3, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + uint64_t addr = semihosting_get_field(target, 1, fields); + size_t len = semihosting_get_field(target, 2, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "read"; + fileio_info->param_1 = fd; + fileio_info->param_2 = addr; + fileio_info->param_3 = len; + } else { + uint8_t *buf = malloc(len); + if (!buf) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + semihosting->result = read(fd, buf, len); + semihosting->sys_errno = errno; + LOG_DEBUG("read(%d, 0x%" PRIx64 ", %zu)=%d", + fd, + addr, + len, + (int)semihosting->result); + if (semihosting->result >= 0) { + retval = target_write_buffer(target, addr, + semihosting->result, + buf); + if (retval != ERROR_OK) { + free(buf); + return retval; + } + /* the number of bytes NOT filled in */ + semihosting->result = len - + semihosting->result; + } + free(buf); + } + } + } + break; + + case SEMIHOSTING_SYS_READC: /* 0x07 */ + /* + * Reads a byte from the console. + * + * Entry + * The PARAMETER REGISTER must contain 0. There are no other + * parameters or values possible. + * + * Return + * On exit, the RETURN REGISTER contains the byte read from + * the console. + */ + if (semihosting->is_fileio) { + LOG_ERROR("SYS_READC not supported by semihosting fileio"); + return ERROR_FAIL; + } + semihosting->result = getchar(); + LOG_DEBUG("getchar()=%d", (int)semihosting->result); + break; + + case SEMIHOSTING_SYS_REMOVE: /* 0x0E */ + /* + * Deletes a specified file on the host filing system. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field argument block: + * - field 1 Points to a null-terminated string that gives the + * path name of the file to be deleted. + * - field 2 The length of the string. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the delete is successful + * - A nonzero, host-specific error code if the delete fails. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + size_t len = semihosting_get_field(target, 1, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "unlink"; + fileio_info->param_1 = addr; + fileio_info->param_2 = len; + } else { + uint8_t *fn = malloc(len+1); + if (!fn) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = + target_read_memory(target, addr, 1, len, + fn); + if (retval != ERROR_OK) { + free(fn); + return retval; + } + fn[len] = 0; + semihosting->result = remove((char *)fn); + semihosting->sys_errno = errno; + LOG_DEBUG("remove('%s')=%d", fn, + (int)semihosting->result); + + free(fn); + } + } + } + break; + + case SEMIHOSTING_SYS_RENAME: /* 0x0F */ + /* + * Renames a specified file. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * four-field data block: + * - field 1 A pointer to the name of the old file. + * - field 2 The length of the old filename. + * - field 3 A pointer to the new filename. + * - field 4 The length of the new filename. Both strings are + * null-terminated. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the rename is successful. + * - A nonzero, host-specific error code if the rename fails. + */ + retval = semihosting_read_fields(target, 4, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr1 = semihosting_get_field(target, 0, fields); + size_t len1 = semihosting_get_field(target, 1, fields); + uint64_t addr2 = semihosting_get_field(target, 2, fields); + size_t len2 = semihosting_get_field(target, 3, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "rename"; + fileio_info->param_1 = addr1; + fileio_info->param_2 = len1; + fileio_info->param_3 = addr2; + fileio_info->param_4 = len2; + } else { + uint8_t *fn1 = malloc(len1+1); + uint8_t *fn2 = malloc(len2+1); + if (!fn1 || !fn2) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = target_read_memory(target, addr1, 1, len1, + fn1); + if (retval != ERROR_OK) { + free(fn1); + free(fn2); + return retval; + } + retval = target_read_memory(target, addr2, 1, len2, + fn2); + if (retval != ERROR_OK) { + free(fn1); + free(fn2); + return retval; + } + fn1[len1] = 0; + fn2[len2] = 0; + semihosting->result = rename((char *)fn1, + (char *)fn2); + semihosting->sys_errno = errno; + LOG_DEBUG("rename('%s', '%s')=%d", fn1, fn2, + (int)semihosting->result); + + free(fn1); + free(fn2); + } + } + } + break; + + case SEMIHOSTING_SYS_SEEK: /* 0x0A */ + /* + * Seeks to a specified position in a file using an offset + * specified from the start of the file. The file is assumed + * to be a byte array and the offset is given in bytes. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field data block: + * - field 1 A handle for a seekable file object. + * - field 2 The absolute byte position to seek to. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the request is successful. + * - A negative value if the request is not successful. + * Use SYS_ERRNO to read the value of the host errno variable + * describing the error. + * + * Note: The effect of seeking outside the current extent of + * the file object is undefined. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + off_t pos = semihosting_get_field(target, 1, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "lseek"; + fileio_info->param_1 = fd; + fileio_info->param_2 = pos; + fileio_info->param_3 = SEEK_SET; + } else { + semihosting->result = lseek(fd, pos, SEEK_SET); + semihosting->sys_errno = errno; + LOG_DEBUG("lseek(%d, %d)=%d", fd, (int)pos, + (int)semihosting->result); + if (semihosting->result == pos) + semihosting->result = 0; + } + } + break; + + case SEMIHOSTING_SYS_SYSTEM: /* 0x12 */ + /* + * Passes a command to the host command-line interpreter. + * This enables you to execute a system command such as dir, + * ls, or pwd. The terminal I/O is on the host, and is not + * visible to the target. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field argument block: + * - field 1 Points to a string to be passed to the host + * command-line interpreter. + * - field 2 The length of the string. + * + * Return + * On exit, the RETURN REGISTER contains the return status. + */ + + /* Provide SYS_SYSTEM functionality. Uses the + * libc system command, there may be a reason *NOT* + * to use this, but as I can't think of one, I + * implemented it this way. + */ + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) + return retval; + else { + uint64_t addr = semihosting_get_field(target, 0, fields); + size_t len = semihosting_get_field(target, 1, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "system"; + fileio_info->param_1 = addr; + fileio_info->param_2 = len; + } else { + uint8_t *cmd = malloc(len+1); + if (!cmd) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = target_read_memory(target, + addr, + 1, + len, + cmd); + if (retval != ERROR_OK) { + free(cmd); + return retval; + } else { + cmd[len] = 0; + semihosting->result = system( + (const char *)cmd); + LOG_DEBUG("system('%s')=%d", + cmd, + (int)semihosting->result); + } + + free(cmd); + } + } + } + break; + + case SEMIHOSTING_SYS_TIME: /* 0x11 */ + /* + * Returns the number of seconds since 00:00 January 1, 1970. + * This value is real-world time, regardless of any debug agent + * configuration. + * + * Entry + * There are no parameters. + * + * Return + * On exit, the RETURN REGISTER contains the number of seconds. + */ + semihosting->result = time(NULL); + break; + + case SEMIHOSTING_SYS_WRITE: /* 0x05 */ + /* + * Writes the contents of a buffer to a specified file at the + * current file position. The file position is specified either: + * - Explicitly, by a SYS_SEEK. + * - Implicitly as one byte beyond the previous SYS_READ or + * SYS_WRITE request. + * + * The file position is at the start of the file when the file + * is opened, and is lost when the file is closed. + * + * Perform the file operation as a single action whenever + * possible. For example, do not split a write of 16KB into + * four 4KB chunks unless there is no alternative. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * three-field data block: + * - field 1 Contains a handle for a file previously opened + * with SYS_OPEN. + * - field 2 Points to the memory containing the data to be written. + * - field 3 Contains the number of bytes to be written from + * the buffer to the file. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the call is successful. + * - The number of bytes that are not written, if there is an error. + */ + retval = semihosting_read_fields(target, 3, fields); + if (retval != ERROR_OK) + return retval; + else { + int fd = semihosting_get_field(target, 0, fields); + uint64_t addr = semihosting_get_field(target, 1, fields); + size_t len = semihosting_get_field(target, 2, fields); + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "write"; + fileio_info->param_1 = fd; + fileio_info->param_2 = addr; + fileio_info->param_3 = len; + } else { + uint8_t *buf = malloc(len); + if (!buf) { + semihosting->result = -1; + semihosting->sys_errno = ENOMEM; + } else { + retval = target_read_buffer(target, addr, len, buf); + if (retval != ERROR_OK) { + free(buf); + return retval; + } + semihosting->result = write(fd, buf, len); + semihosting->sys_errno = errno; + LOG_DEBUG("write(%d, 0x%" PRIx64 ", %zu)=%d", + fd, + addr, + len, + (int)semihosting->result); + if (semihosting->result >= 0) { + /* The number of bytes that are NOT written. + * */ + semihosting->result = len - + semihosting->result; + } + + free(buf); + } + } + } + break; + + case SEMIHOSTING_SYS_WRITEC: /* 0x03 */ + /* + * Writes a character byte, pointed to by the PARAMETER REGISTER, + * to the debug channel. When executed under a semihosting + * debugger, the character appears on the host debugger console. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to the + * character. + * + * Return + * None. The RETURN REGISTER is corrupted. + */ + if (semihosting->is_fileio) { + semihosting->hit_fileio = true; + fileio_info->identifier = "write"; + fileio_info->param_1 = 1; + fileio_info->param_2 = semihosting->param; + fileio_info->param_3 = 1; + } else { + uint64_t addr = semihosting->param; + unsigned char c; + retval = target_read_memory(target, addr, 1, 1, &c); + if (retval != ERROR_OK) + return retval; + putchar(c); + semihosting->result = 0; + } + break; + + case SEMIHOSTING_SYS_WRITE0: /* 0x04 */ + /* + * Writes a null-terminated string to the debug channel. + * When executed under a semihosting debugger, the characters + * appear on the host debugger console. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to the + * first byte of the string. + * + * Return + * None. The RETURN REGISTER is corrupted. + */ + if (semihosting->is_fileio) { + size_t count = 0; + uint64_t addr = semihosting->param; + for (;; addr++) { + unsigned char c; + retval = target_read_memory(target, addr, 1, 1, &c); + if (retval != ERROR_OK) + return retval; + if (c == '\0') + break; + count++; + } + semihosting->hit_fileio = true; + fileio_info->identifier = "write"; + fileio_info->param_1 = 1; + fileio_info->param_2 = semihosting->param; + fileio_info->param_3 = count; + } else { + uint64_t addr = semihosting->param; + do { + unsigned char c; + retval = target_read_memory(target, addr++, 1, 1, &c); + if (retval != ERROR_OK) + return retval; + if (!c) + break; + putchar(c); + } while (1); + semihosting->result = 0; + } + break; + + case SEMIHOSTING_SYS_ELAPSED: /* 0x30 */ + /* + * Returns the number of elapsed target ticks since execution + * started. + * Use SYS_TICKFREQ to determine the tick frequency. + * + * Entry (32-bit) + * On entry, the PARAMETER REGISTER points to a two-field data + * block to be used for returning the number of elapsed ticks: + * - field 1 The least significant field and is at the low address. + * - field 2 The most significant field and is at the high address. + * + * Entry (64-bit) + * On entry the PARAMETER REGISTER points to a one-field data + * block to be used for returning the number of elapsed ticks: + * - field 1 The number of elapsed ticks as a 64-bit value. + * + * Return + * On exit: + * - On success, the RETURN REGISTER contains 0, the PARAMETER + * REGISTER is unchanged, and the data block pointed to by the + * PARAMETER REGISTER is filled in with the number of elapsed + * ticks. + * - On failure, the RETURN REGISTER contains -1, and the + * PARAMETER REGISTER contains -1. + * + * Note: Some semihosting implementations might not support this + * semihosting operation, and they always return -1 in the + * RETURN REGISTER. + */ + + case SEMIHOSTING_SYS_TICKFREQ: /* 0x31 */ + /* + * Returns the tick frequency. + * + * Entry + * The PARAMETER REGISTER must contain 0 on entry to this routine. + * + * Return + * On exit, the RETURN REGISTER contains either: + * - The number of ticks per second. + * - –1 if the target does not know the value of one tick. + * + * Note: Some semihosting implementations might not support + * this semihosting operation, and they always return -1 in the + * RETURN REGISTER. + */ + + case SEMIHOSTING_SYS_TMPNAM: /* 0x0D */ + /* + * Returns a temporary name for a file identified by a system + * file identifier. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * three-word argument block: + * - field 1 A pointer to a buffer. + * - field 2 A target identifier for this filename. Its value + * must be an integer in the range 0-255. + * - field 3 Contains the length of the buffer. The length must + * be at least the value of L_tmpnam on the host system. + * + * Return + * On exit, the RETURN REGISTER contains: + * - 0 if the call is successful. + * - –1 if an error occurs. + * + * The buffer pointed to by the PARAMETER REGISTER contains + * the filename, prefixed with a suitable directory name. + * If you use the same target identifier again, the same + * filename is returned. + * + * Note: The returned string must be null-terminated. + */ + + default: + fprintf(stderr, "semihosting: unsupported call %#x\n", + (unsigned) semihosting->op); + semihosting->result = -1; + semihosting->sys_errno = ENOTSUP; + } + + if (!semihosting->hit_fileio) { + retval = semihosting->post_result(target); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to post semihosting result"); + return retval; + } + } + + return ERROR_OK; +} + +/* ------------------------------------------------------------------------- + * Local functions. */ + +static int semihosting_common_fileio_info(struct target *target, + struct gdb_fileio_info *fileio_info) +{ + struct semihosting *semihosting = target->semihosting; + if (!semihosting) + return ERROR_FAIL; + + /* + * To avoid unnecessary duplication, semihosting prepares the + * fileio_info structure out-of-band when the target halts. See + * do_semihosting for more detail. + */ + if (!semihosting->is_fileio || !semihosting->hit_fileio) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int semihosting_common_fileio_end(struct target *target, int result, + int fileio_errno, bool ctrl_c) +{ + struct gdb_fileio_info *fileio_info = target->fileio_info; + struct semihosting *semihosting = target->semihosting; + if (!semihosting) + return ERROR_FAIL; + + /* clear pending status */ + semihosting->hit_fileio = false; + + semihosting->result = result; + semihosting->sys_errno = fileio_errno; + + /* + * Some fileio results do not match up with what the semihosting + * operation expects; for these operations, we munge the results + * below: + */ + switch (semihosting->op) { + case SEMIHOSTING_SYS_WRITE: /* 0x05 */ + if (result < 0) + semihosting->result = fileio_info->param_3; + else + semihosting->result = 0; + break; + + case SEMIHOSTING_SYS_READ: /* 0x06 */ + if (result == (int)fileio_info->param_3) + semihosting->result = 0; + if (result <= 0) + semihosting->result = fileio_info->param_3; + break; + + case SEMIHOSTING_SYS_SEEK: /* 0x0a */ + if (result > 0) + semihosting->result = 0; + break; + } + + return semihosting->post_result(target); +} + +/** + * Read all fields of a command from target to buffer. + */ +static int semihosting_read_fields(struct target *target, size_t number, + uint8_t *fields) +{ + struct semihosting *semihosting = target->semihosting; + return target_read_memory(target, semihosting->param, + semihosting->word_size_bytes, number, fields); +} + +/** + * Write all fields of a command from buffer to target. + */ +static int semihosting_write_fields(struct target *target, size_t number, + uint8_t *fields) +{ + struct semihosting *semihosting = target->semihosting; + return target_write_memory(target, semihosting->param, + semihosting->word_size_bytes, number, fields); +} + +/** + * Extract a field from the buffer, considering register size and endianness. + */ +static uint64_t semihosting_get_field(struct target *target, size_t index, + uint8_t *fields) +{ + struct semihosting *semihosting = target->semihosting; + if (semihosting->word_size_bytes == 8) + return target_buffer_get_u64(target, fields + (index * 8)); + else + return target_buffer_get_u32(target, fields + (index * 4)); +} + +/** + * Store a field in the buffer, considering register size and endianness. + */ +static void semihosting_set_field(struct target *target, uint64_t value, + size_t index, + uint8_t *fields) +{ + struct semihosting *semihosting = target->semihosting; + if (semihosting->word_size_bytes == 8) + target_buffer_set_u64(target, fields + (index * 8), value); + else + target_buffer_set_u32(target, fields + (index * 4), value); +} + + +/* ------------------------------------------------------------------------- + * Common semihosting commands handlers. */ + +__COMMAND_HANDLER(handle_common_semihosting_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD_CTX, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + if (CMD_ARGC > 0) { + int is_active; + + COMMAND_PARSE_ENABLE(CMD_ARGV[0], is_active); + + if (!target_was_examined(target)) { + LOG_ERROR("Target not examined yet"); + return ERROR_FAIL; + } + + if (semihosting && semihosting->setup(target, is_active) != ERROR_OK) { + LOG_ERROR("Failed to Configure semihosting"); + return ERROR_FAIL; + } + + /* FIXME never let that "catch" be dropped! (???) */ + semihosting->is_active = is_active; + } + + command_print(CMD_CTX, "semihosting is %s", + semihosting->is_active + ? "enabled" : "disabled"); + + return ERROR_OK; +} + + +__COMMAND_HANDLER(handle_common_semihosting_fileio_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD_CTX, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + if (!semihosting->is_active) { + command_print(CMD_CTX, "semihosting not yet enabled for current target"); + return ERROR_FAIL; + } + + if (CMD_ARGC > 0) + COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->is_fileio); + + command_print(CMD_CTX, "semihosting fileio is %s", + semihosting->is_fileio + ? "enabled" : "disabled"); + + return ERROR_OK; +} + +__COMMAND_HANDLER(handle_common_semihosting_cmdline) +{ + struct target *target = get_current_target(CMD_CTX); + unsigned int i; + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD_CTX, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + free(semihosting->cmdline); + semihosting->cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL; + + for (i = 1; i < CMD_ARGC; i++) { + char *cmdline = alloc_printf("%s %s", semihosting->cmdline, CMD_ARGV[i]); + if (cmdline == NULL) + break; + free(semihosting->cmdline); + semihosting->cmdline = cmdline; + } + + command_print(CMD_CTX, "semihosting command line is [%s]", + semihosting->cmdline); + + return ERROR_OK; +} + +__COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD_CTX, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + if (!semihosting->is_active) { + command_print(CMD_CTX, "semihosting not yet enabled for current target"); + return ERROR_FAIL; + } + + if (CMD_ARGC > 0) + COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting->has_resumable_exit); + + command_print(CMD_CTX, "semihosting resumable exit is %s", + semihosting->has_resumable_exit + ? "enabled" : "disabled"); + + return ERROR_OK; +} diff --git a/src/target/semihosting_common.h b/src/target/semihosting_common.h new file mode 100644 index 000000000..8fb5e0c3a --- /dev/null +++ b/src/target/semihosting_common.h @@ -0,0 +1,163 @@ +/*************************************************************************** + * Copyright (C) 2018 by Liviu Ionescu * + * * + * * + * Copyright (C) 2009 by Marvell Technology Group Ltd. * + * Written by Nicolas Pitre * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_SEMIHOSTING_COMMON_H +#define OPENOCD_TARGET_SEMIHOSTING_COMMON_H + +#include +#include +#include + +/* + * According to: + * "Semihosting for AArch32 and AArch64, Release 2.0" + * https://static.docs.arm.com/100863/0200/semihosting.pdf + * from ARM Ltd. + * + * The available semihosting operation numbers passed in R0 are allocated + * as follows: + * - 0x00-0x31 Used by ARM. + * - 0x32-0xFF Reserved for future use by ARM. + * - 0x100-0x1FF Reserved for user applications. These are not used by ARM. + * However, if you are writing your own SVC operations, you are advised + * to use a different SVC number rather than using the semihosted + * SVC number and these operation type numbers. + * - 0x200-0xFFFFFFFF Undefined and currently unused. It is recommended + * that you do not use these. + */ + +enum semihosting_operation_numbers { + /* + * ARM semihosting operations, in lexicographic order. + */ + SEMIHOSTING_ENTER_SVC = 0x17, /* DEPRECATED */ + + SEMIHOSTING_SYS_CLOSE = 0x02, + SEMIHOSTING_SYS_CLOCK = 0x10, + SEMIHOSTING_SYS_ELAPSED = 0x30, + SEMIHOSTING_SYS_ERRNO = 0x13, + SEMIHOSTING_SYS_EXIT = 0x18, + SEMIHOSTING_SYS_EXIT_EXTENDED = 0x20, + SEMIHOSTING_SYS_FLEN = 0x0C, + SEMIHOSTING_SYS_GET_CMDLINE = 0x15, + SEMIHOSTING_SYS_HEAPINFO = 0x16, + SEMIHOSTING_SYS_ISERROR = 0x08, + SEMIHOSTING_SYS_ISTTY = 0x09, + SEMIHOSTING_SYS_OPEN = 0x01, + SEMIHOSTING_SYS_READ = 0x06, + SEMIHOSTING_SYS_READC = 0x07, + SEMIHOSTING_SYS_REMOVE = 0x0E, + SEMIHOSTING_SYS_RENAME = 0x0F, + SEMIHOSTING_SYS_SEEK = 0x0A, + SEMIHOSTING_SYS_SYSTEM = 0x12, + SEMIHOSTING_SYS_TICKFREQ = 0x31, + SEMIHOSTING_SYS_TIME = 0x11, + SEMIHOSTING_SYS_TMPNAM = 0x0D, + SEMIHOSTING_SYS_WRITE = 0x05, + SEMIHOSTING_SYS_WRITEC = 0x03, + SEMIHOSTING_SYS_WRITE0 = 0x04, +}; + +/* + * Codes used by SEMIHOSTING_SYS_EXIT (formerly + * SEMIHOSTING_REPORT_EXCEPTION). + * On 64-bits, the exit code is passed explicitly. + */ +enum semihosting_reported_exceptions { + /* On 32 bits, use it for exit(0) */ + ADP_STOPPED_APPLICATION_EXIT = ((2 << 16) + 38), + /* On 32 bits, use it for exit(1) */ + ADP_STOPPED_RUN_TIME_ERROR = ((2 << 16) + 35), +}; + +struct target; + +/* + * A pointer to this structure was added to the target structure. + */ +struct semihosting { + + /** A flag reporting whether semihosting is active. */ + bool is_active; + + /** A flag reporting whether semihosting fileio is active. */ + bool is_fileio; + + /** A flag reporting whether semihosting fileio operation is active. */ + bool hit_fileio; + + /** Most are resumable, except the two exit calls. */ + bool is_resumable; + + /** + * When SEMIHOSTING_SYS_EXIT is called outside a debug session, + * things are simple, the openocd process calls exit() and passes + * the value returned by the target. + * When SEMIHOSTING_SYS_EXIT is called during a debug session, + * by default execution returns to the debugger, leaving the + * debugger in a HALT state, similar to the state entered when + * encountering a break. + * In some use cases, it is useful to have SEMIHOSTING_SYS_EXIT + * return normally, as any semihosting call, and do not break + * to the debugger. + * The standard allows this to happen, but the condition + * to trigger it is a bit obscure ("by performing an RDI_Execute + * request or equivalent"). + * + * To make the SEMIHOSTING_SYS_EXIT call return normally, enable + * this variable via the dedicated command (default: disabled). + */ + bool has_resumable_exit; + + /** The Target (hart) word size; 8 for 64-bits targets. */ + size_t word_size_bytes; + + /** The current semihosting operation (R0 on ARM). */ + int op; + + /** The current semihosting parameter (R1 or ARM). */ + uint64_t param; + + /** + * The current semihosting result to be returned to the application. + * Usually 0 for success, -1 for error, + * but sometimes a useful value, even a pointer. + */ + int64_t result; + + /** The value to be returned by semihosting SYS_ERRNO request. */ + int sys_errno; + + /** The semihosting command line to be passed to the target. */ + char *cmdline; + + /** The current time when 'execution starts' */ + clock_t setup_time; + + int (*setup)(struct target *target, int enable); + int (*post_result)(struct target *target); +}; + +int semihosting_common_init(struct target *target, void *setup, + void *post_result); +int semihosting_common(struct target *target); + +#endif /* OPENOCD_TARGET_SEMIHOSTING_COMMON_H */ diff --git a/src/target/target.h b/src/target/target.h index c5fb55ba7..51a5b6935 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -205,6 +205,9 @@ struct target { /* file-I/O information for host to do syscall */ struct gdb_fileio_info *fileio_info; + + /* The semihosting information, extracted from the target. */ + struct semihosting *semihosting; }; struct target_list { @@ -214,10 +217,10 @@ struct target_list { struct gdb_fileio_info { char *identifier; - uint32_t param_1; - uint32_t param_2; - uint32_t param_3; - uint32_t param_4; + uint64_t param_1; + uint64_t param_2; + uint64_t param_3; + uint64_t param_4; }; /** Returns the instance-specific name of the specified target. */