aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/gdb_server.c26
-rw-r--r--src/target/Makefile.am2
-rw-r--r--src/target/arm.h25
-rw-r--r--src/target/arm_semihosting.c580
-rw-r--r--src/target/arm_semihosting.h2
-rw-r--r--src/target/armv4_5.c141
-rw-r--r--src/target/armv7m.c10
-rw-r--r--src/target/armv8.c6
-rw-r--r--src/target/nds32.c73
-rw-r--r--src/target/semihosting_common.c1579
-rw-r--r--src/target/semihosting_common.h163
-rw-r--r--src/target/target.h11
12 files changed, 1871 insertions, 747 deletions
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index d329bfe..f54db9e 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 206b281..fcc23ad 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 dd25d53..316ff9a 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
+ * <ilg@livius.net>
+ *
* 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 <helper/command.h>
#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 f31f901..57f3139 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 <stallion@squareup.com> *
* *
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * <ilg@livius.net> *
+ * *
* 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 <helper/log.h>
#include <sys/stat.h>
-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 011f19f..cf1f8de 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 06994ca..5ee8ead 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 *
+ * <ilg@livius.net> *
+ * *
* 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 <helper/binarybuffer.h>
#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 7b7893f..1b4e5b1 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 *
+ * <ilg@livius.net> *
+ * *
* 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 82b2b24..20f2b67 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 *
+ * <ilg@livius.net> *
+ * *
* 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 e4bb17f..4115ea4 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, &reg_r0);
+ nds32_get_mapped_reg(nds32, R1, &reg_r1);
+ nds32_get_mapped_reg(nds32, R2, &reg_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 0000000..beeb474
--- /dev/null
+++ b/src/target/semihosting_common.c
@@ -0,0 +1,1579 @@
+/***************************************************************************
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * <ilg@livius.net> *
+ * *
+ * Copyright (C) 2018 by Marvell Technology Group Ltd. *
+ * Written by Nicolas Pitre <nico@marvell.com> *
+ * *
+ * Copyright (C) 2010 by Spencer Oliver *
+ * spen@spen-soft.co.uk *
+ * *
+ * Copyright (C) 2016 by Square, Inc. *
+ * Steven Stallion <stallion@squareup.com> *
+ * *
+ * 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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+/**
+ * @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 <helper/binarybuffer.h>
+#include <helper/log.h>
+#include <sys/stat.h>
+
+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 0000000..8fb5e0c
--- /dev/null
+++ b/src/target/semihosting_common.h
@@ -0,0 +1,163 @@
+/***************************************************************************
+ * Copyright (C) 2018 by Liviu Ionescu *
+ * <ilg@livius.net> *
+ * *
+ * Copyright (C) 2009 by Marvell Technology Group Ltd. *
+ * Written by Nicolas Pitre <nico@marvell.com> *
+ * *
+ * 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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_SEMIHOSTING_COMMON_H
+#define OPENOCD_TARGET_SEMIHOSTING_COMMON_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+
+/*
+ * 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 c5fb55b..51a5b69 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. */