From c0e7ccbd87cf491b065bf18bbdb6a73b741c2698 Mon Sep 17 00:00:00 2001 From: Steven Stallion Date: Fri, 22 Jul 2016 15:43:11 -0500 Subject: [PATCH] semihosting: support fileio operation This patch adds support for bridging semihosting to GDB's File-I/O remote protocol extension. For the most part operations match up 1:1, however some require a working area to complete successfully, namely operations that devolve to read, stat, and gettimeofday. A new command was added to enable support for fileio named `arm semihosting_fileio`, which ensures that the default behavior remains intact for those that prefer it. Finally, redundant logging was removed from the target_arch_state function; this permits ARM targets to quiesce log output when polling for a fileio reply. This prevents filling the logs with halt/resume messages when using semihosting fileio. Change-Id: Ifbb864fc2373336a501cc0332675b887b552e1ee Signed-off-by: Steven Stallion Reviewed-on: http://openocd.zylin.com/3566 Tested-by: jenkins Reviewed-by: Steven Stallion Reviewed-by: Paul Fertser --- doc/openocd.texi | 11 + src/target/arm.h | 12 + src/target/arm7tdmi.c | 3 +- src/target/arm9tdmi.c | 2 + src/target/arm_semihosting.c | 512 +++++++++++++++++++++++++---------- src/target/arm_semihosting.h | 1 + src/target/armv4_5.c | 47 +++- src/target/armv7m.c | 9 +- src/target/cortex_a.c | 1 + src/target/cortex_m.c | 1 + src/target/hla_target.c | 2 +- src/target/target.c | 5 +- 12 files changed, 448 insertions(+), 158 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 372683a9cc..1cca4ab11a 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -7404,6 +7404,17 @@ requests by using a special SVC instruction that is trapped at the Supervisor Call vector by OpenOCD. @end deffn +@deffn Command {arm semihosting_fileio} [@option{enable}|@option{disable}] +@cindex ARM semihosting +Display status of semihosting fileio, after optionally changing that +status. + +Enabling this option forwards semihosting I/O to GDB process using the +File-I/O remote protocol extension. This is especially useful for +interacting with remote files or displaying console messages in the +debugger. +@end deffn + @section ARMv4 and ARMv5 Architecture @cindex ARMv4 @cindex ARMv5 diff --git a/src/target/arm.h b/src/target/arm.h index 28022e3d2d..226dd65689 100644 --- a/src/target/arm.h +++ b/src/target/arm.h @@ -130,6 +130,18 @@ struct arm { /** 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; + + /** Current semihosting operation. */ + int semihosting_op; + + /** Current semihosting result. */ + int semihosting_result; + /** Value to be returned by semihosting SYS_ERRNO request. */ int semihosting_errno; diff --git a/src/target/arm7tdmi.c b/src/target/arm7tdmi.c index 58ab027966..9dcb302d9c 100644 --- a/src/target/arm7tdmi.c +++ b/src/target/arm7tdmi.c @@ -30,6 +30,7 @@ #include "target_type.h" #include "register.h" #include "arm_opcodes.h" +#include "arm_semihosting.h" /* * For information about ARM7TDMI, see ARM DDI 0210C (r4p1) @@ -615,7 +616,7 @@ static void arm7tdmi_build_reg_cache(struct target *target) int arm7tdmi_init_target(struct command_context *cmd_ctx, struct target *target) { arm7tdmi_build_reg_cache(target); - + arm_semihosting_init(target); return ERROR_OK; } diff --git a/src/target/arm9tdmi.c b/src/target/arm9tdmi.c index eed965a376..82b430f88d 100644 --- a/src/target/arm9tdmi.c +++ b/src/target/arm9tdmi.c @@ -30,6 +30,7 @@ #include "target_type.h" #include "register.h" #include "arm_opcodes.h" +#include "arm_semihosting.h" /* * NOTE: this holds code that's used with multiple ARM9 processors: @@ -714,6 +715,7 @@ int arm9tdmi_init_target(struct command_context *cmd_ctx, struct target *target) { arm9tdmi_build_reg_cache(target); + arm_semihosting_init(target); return ERROR_OK; } diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c index 63335ff3c7..252511962d 100644 --- a/src/target/arm_semihosting.c +++ b/src/target/arm_semihosting.c @@ -5,6 +5,9 @@ * 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 * @@ -43,6 +46,7 @@ #include "cortex_m.h" #include "register.h" #include "arm_opcodes.h" +#include "target_type.h" #include "arm_semihosting.h" #include #include @@ -63,13 +67,59 @@ static const int open_modeflags[12] = { O_RDWR | O_CREAT | O_APPEND | O_BINARY }; +static int post_result(struct target *target) +{ + struct arm *arm = target_to_arm(target); + + /* REVISIT this looks wrong ... ARM11 and Cortex-A8 + * should work this way at least sometimes. + */ + if (is_arm7_9(target_to_arm7_9(target)) || + is_armv7a(target_to_armv7a(target))) { + uint32_t spsr; + + /* return value in R0 */ + buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result); + arm->core_cache->reg_list[0].dirty = 1; + + /* LR --> PC */ + buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32, + buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32)); + arm->core_cache->reg_list[15].dirty = 1; + + /* saved PSR --> current PSR */ + spsr = buf_get_u32(arm->spsr->value, 0, 32); + + /* REVISIT should this be arm_set_cpsr(arm, spsr) + * instead of a partially unrolled version? + */ + + buf_set_u32(arm->cpsr->value, 0, 32, spsr); + arm->cpsr->dirty = 1; + arm->core_mode = spsr & 0x1f; + if (spsr & 0x20) + arm->core_state = ARM_STATE_THUMB; + + } else { + /* resume execution, this will be pc+2 to skip over the + * bkpt instruction */ + + /* return result in R0 */ + buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->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, result; + int retval; /* * TODO: lots of security issues are not considered yet, such as: @@ -77,10 +127,10 @@ static int do_semihosting(struct target *target) * - no safety checks on opened/deleted/renamed file paths * Beware the target app you use this support with. * - * TODO: explore mapping requests to GDB's "File-I/O Remote - * Protocol Extension" ... when GDB is active. + * TODO: unsupported semihosting fileio operations could be + * implemented if we had a small working area at our disposal. */ - switch (r0) { + switch ((arm->semihosting_op = r0)) { case 0x01: /* SYS_OPEN */ retval = target_read_memory(target, r1, 4, 3, params); if (retval != ERROR_OK) @@ -89,27 +139,40 @@ static int do_semihosting(struct target *target) 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); - if (l <= 255 && m <= 11) { - uint8_t fn[256]; - retval = target_read_memory(target, a, 1, l, fn); - if (retval != ERROR_OK) - return retval; - fn[l] = 0; - if (strcmp((char *)fn, ":tt") == 0) { - if (m < 4) - result = dup(STDIN_FILENO); - else - result = dup(STDOUT_FILENO); - } else { - /* cygwin requires the permission setting - * otherwise it will fail to reopen a previously - * written file */ - result = open((char *)fn, open_modeflags[m], 0644); + 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; } - arm->semihosting_errno = errno; } else { - result = -1; - arm->semihosting_errno = EINVAL; + 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; @@ -120,33 +183,63 @@ static int do_semihosting(struct target *target) return retval; else { int fd = target_buffer_get_u32(target, params+0); - result = close(fd); - arm->semihosting_errno = errno; + 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); - result = 0; + arm->semihosting_result = 0; } break; case 0x04: /* SYS_WRITE0 */ - 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); - result = 0; + 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 */ @@ -157,21 +250,29 @@ static int do_semihosting(struct target *target) 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); - uint8_t *buf = malloc(l); - if (!buf) { - result = -1; - arm->semihosting_errno = ENOMEM; + 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 { - retval = target_read_buffer(target, a, l, buf); - if (retval != ERROR_OK) { + 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); - return retval; } - result = write(fd, buf, l); - arm->semihosting_errno = errno; - if (result >= 0) - result = l - result; - free(buf); } } break; @@ -184,42 +285,60 @@ static int do_semihosting(struct target *target) 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); - uint8_t *buf = malloc(l); - if (!buf) { - result = -1; - arm->semihosting_errno = ENOMEM; + 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 { - result = read(fd, buf, l); - arm->semihosting_errno = errno; - if (result >= 0) { - retval = target_write_buffer(target, a, result, buf); - if (retval != ERROR_OK) { - free(buf); - return retval; + 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; } - result = l - result; + free(buf); } - free(buf); } } break; case 0x07: /* SYS_READC */ - result = getchar(); + 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; - result = (target_buffer_get_u32(target, params+0) != 0); + arm->semihosting_result = (target_buffer_get_u32(target, params+0) != 0); break; case 0x09: /* SYS_ISTTY */ - retval = target_read_memory(target, r1, 4, 1, params); - if (retval != ERROR_OK) - return retval; - result = isatty(target_buffer_get_u32(target, params+0)); + 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 */ @@ -229,27 +348,39 @@ static int do_semihosting(struct target *target) else { int fd = target_buffer_get_u32(target, params+0); off_t pos = target_buffer_get_u32(target, params+4); - result = lseek(fd, pos, SEEK_SET); - arm->semihosting_errno = errno; - if (result == pos) - result = 0; + 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; - result = fstat(fd, &buf); - if (result == -1) { + arm->semihosting_result = fstat(fd, &buf); + if (arm->semihosting_result == -1) { arm->semihosting_errno = errno; - result = -1; + arm->semihosting_result = -1; break; } - result = buf.st_size; + arm->semihosting_result = buf.st_size; } break; @@ -260,17 +391,24 @@ static int do_semihosting(struct target *target) else { uint32_t a = target_buffer_get_u32(target, params+0); uint32_t l = target_buffer_get_u32(target, params+4); - 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; - result = remove((char *)fn); - arm->semihosting_errno = errno; + 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 { - result = -1; - arm->semihosting_errno = EINVAL; + 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; @@ -284,31 +422,40 @@ static int do_semihosting(struct target *target) 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 (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; - result = rename((char *)fn1, (char *)fn2); - arm->semihosting_errno = errno; + 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 { - result = -1; - arm->semihosting_errno = EINVAL; + 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 */ - result = time(NULL); + arm->semihosting_result = time(NULL); break; case 0x13: /* SYS_ERRNO */ - result = arm->semihosting_errno; + arm->semihosting_result = arm->semihosting_errno; break; case 0x15: /* SYS_GET_CMDLINE */ @@ -321,12 +468,12 @@ static int do_semihosting(struct target *target) char *arg = "foobar"; uint32_t s = strlen(arg) + 1; if (l < s) - result = -1; + arm->semihosting_result = -1; else { retval = target_write_buffer(target, a, s, (uint8_t *)arg); if (retval != ERROR_OK) return retval; - result = 0; + arm->semihosting_result = 0; } } break; @@ -342,7 +489,7 @@ static int do_semihosting(struct target *target) retval = target_write_memory(target, a, 4, 4, params); if (retval != ERROR_OK) return retval; - result = 0; + arm->semihosting_result = 0; } break; @@ -386,17 +533,24 @@ static int do_semihosting(struct target *target) else { uint32_t len = target_buffer_get_u32(target, params+4); uint32_t c_ptr = target_buffer_get_u32(target, params); - uint8_t cmd[256]; - if (len > 255) { - result = -1; - arm->semihosting_errno = EINVAL; + 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 { - memset(cmd, 0x0, 256); - retval = target_read_memory(target, c_ptr, 1, len, cmd); - if (retval != ERROR_OK) - return retval; - else - result = system((const char *)cmd); + 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; @@ -408,51 +562,84 @@ static int do_semihosting(struct target *target) default: fprintf(stderr, "semihosting: unsupported call %#x\n", (unsigned) r0); - result = -1; + arm->semihosting_result = -1; arm->semihosting_errno = ENOTSUP; } - /* resume execution to the original mode */ + return ERROR_OK; +} - /* REVISIT this looks wrong ... ARM11 and Cortex-A8 - * should work this way at least sometimes. +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 (is_arm7_9(target_to_arm7_9(target)) || - is_armv7a(target_to_armv7a(target))) { - uint32_t spsr; + if (!arm->is_semihosting_fileio || !arm->semihosting_hit_fileio) + return ERROR_FAIL; - /* return value in R0 */ - buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result); - arm->core_cache->reg_list[0].dirty = 1; + return ERROR_OK; +} - /* LR --> PC */ - buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32, - buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32)); - arm->core_cache->reg_list[15].dirty = 1; +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; - /* saved PSR --> current PSR */ - spsr = buf_get_u32(arm->spsr->value, 0, 32); + /* clear pending status */ + arm->semihosting_hit_fileio = false; - /* REVISIT should this be arm_set_cpsr(arm, spsr) - * instead of a partially unrolled version? - */ + arm->semihosting_result = result; + arm->semihosting_errno = fileio_errno; - buf_set_u32(arm->cpsr->value, 0, 32, spsr); - arm->cpsr->dirty = 1; - arm->core_mode = spsr & 0x1f; - if (spsr & 0x20) - arm->core_state = ARM_STATE_THUMB; + /* 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; - } else { - /* resume execution, this will be pc+2 to skip over the - * bkpt instruction */ + 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; - /* return result in R0 */ - buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result); - arm->core_cache->reg_list[0].dirty = 1; + case 0x0a: /* SYS_SEEK */ + if (result > 0) + arm->semihosting_result = 0; + break; } - return target_resume(target, 1, 0, 0, 0); + return post_result(target); +} + +/** + * Initialize ARM semihosting support. + * + * @param target Pointer to the ARM target to initialize. + * @return An error status if there is a problem during initialization. + */ +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; + + return ERROR_OK; } /** @@ -576,6 +763,35 @@ int arm_semihosting(struct target *target, int *retval) return 0; } - *retval = do_semihosting(target); - return 1; + /* 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"); + return 0; + } + } + + /* 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; + } + + *retval = target_resume(target, 1, 0, 0, 0); + if (*retval != ERROR_OK) { + LOG_ERROR("Failed to resume target"); + return 0; + } + + return 1; + } + + return 0; } diff --git a/src/target/arm_semihosting.h b/src/target/arm_semihosting.h index 7b5c0b2de9..011f19f00c 100644 --- a/src/target/arm_semihosting.h +++ b/src/target/arm_semihosting.h @@ -19,6 +19,7 @@ #ifndef OPENOCD_TARGET_ARM_SEMIHOSTING_H #define OPENOCD_TARGET_ARM_SEMIHOSTING_H +int arm_semihosting_init(struct target *target); int arm_semihosting(struct target *target, int *retval); #endif /* OPENOCD_TARGET_ARM_SEMIHOSTING_H */ diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c index 1a31d40ce2..624a2543db 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -666,14 +666,19 @@ int arm_arch_state(struct target *target) return ERROR_FAIL; } + /* avoid filling log waiting for fileio reply */ + if (arm->semihosting_hit_fileio) + return ERROR_OK; + LOG_USER("target halted in %s state due to %s, current mode: %s\n" - "cpsr: 0x%8.8" PRIx32 " pc: 0x%8.8" PRIx32 "%s", + "cpsr: 0x%8.8" PRIx32 " pc: 0x%8.8" PRIx32 "%s%s", arm_state_strings[arm->core_state], debug_reason_name(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 ? ", semihosting" : "", + arm->is_semihosting_fileio ? " fileio" : ""); return ERROR_OK; } @@ -1055,6 +1060,37 @@ COMMAND_HANDLER(handle_arm_semihosting_command) 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; +} + static const struct command_registration arm_exec_command_handlers[] = { { .name = "reg", @@ -1097,6 +1133,13 @@ static const struct command_registration arm_exec_command_handlers[] = { .usage = "['enable'|'disable']", .help = "activate support for semihosting operations", }, + { + "semihosting_fileio", + .handler = handle_arm_semihosting_fileio_command, + .mode = COMMAND_EXEC, + .usage = "['enable'|'disable']", + .help = "activate support for semihosting fileio operations", + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/armv7m.c b/src/target/armv7m.c index 60b244aae3..64d18d75aa 100644 --- a/src/target/armv7m.c +++ b/src/target/armv7m.c @@ -536,11 +536,15 @@ int armv7m_arch_state(struct target *target) struct arm *arm = &armv7m->arm; uint32_t ctrl, sp; + /* avoid filling log waiting for fileio reply */ + if (arm->semihosting_hit_fileio) + return ERROR_OK; + ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32); sp = buf_get_u32(arm->core_cache->reg_list[ARMV7M_R13].value, 0, 32); LOG_USER("target halted due to %s, current mode: %s %s\n" - "xPSR: %#8.8" PRIx32 " pc: %#8.8" PRIx32 " %csp: %#8.8" PRIx32 "%s", + "xPSR: %#8.8" PRIx32 " pc: %#8.8" PRIx32 " %csp: %#8.8" PRIx32 "%s%s", debug_reason_name(target), arm_mode_name(arm->core_mode), armv7m_exception_string(armv7m->exception_number), @@ -548,7 +552,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 ? ", semihosting" : "", + arm->is_semihosting_fileio ? " fileio" : ""); return ERROR_OK; } diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index 91fa48e1ad..ed2f613770 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -3103,6 +3103,7 @@ static int cortex_a_init_target(struct command_context *cmd_ctx, struct target *target) { /* examine_first() does a bunch of this */ + arm_semihosting_init(target); return ERROR_OK; } diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index 4270f8f332..f674cc527c 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -1696,6 +1696,7 @@ static int cortex_m_init_target(struct command_context *cmd_ctx, struct target *target) { armv7m_build_reg_cache(target); + arm_semihosting_init(target); return ERROR_OK; } diff --git a/src/target/hla_target.c b/src/target/hla_target.c index e02abc42ed..feeb11f667 100644 --- a/src/target/hla_target.c +++ b/src/target/hla_target.c @@ -341,7 +341,7 @@ static int adapter_init_target(struct command_context *cmd_ctx, LOG_DEBUG("%s", __func__); armv7m_build_reg_cache(target); - + arm_semihosting_init(target); return ERROR_OK; } diff --git a/src/target/target.c b/src/target/target.c index 26bae4b69a..17902a51c8 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -1944,13 +1944,10 @@ int target_arch_state(struct target *target) { int retval; if (target == NULL) { - LOG_USER("No target has been configured"); + LOG_WARNING("No target has been configured"); return ERROR_OK; } - LOG_USER("%s: target state: %s", target_name(target), - target_state_name(target)); - if (target->state != TARGET_HALTED) return ERROR_OK; -- 2.30.2