]> git.sur5r.net Git - openocd/commitdiff
basic ARM semihosting support
authorNicolas Pitre <nico@fluxnic.net>
Thu, 3 Dec 2009 22:27:13 +0000 (17:27 -0500)
committerDavid Brownell <dbrownell@users.sourceforge.net>
Fri, 4 Dec 2009 02:42:01 +0000 (18:42 -0800)
Semihosting enables code running on an ARM target to use the
I/O facilities on the host computer. The target application must
be linked against a library that forwards operation requests by
using the SVC instruction that is trapped at the Supervisor Call
vector by the debugger.  The "hosted" library version provided
with CodeSourcery's Sourcery G++ Lite for ARM EABI is one example.

This is currently available for ARM9 processors, but any ARM
variant should be able to support this with little additional work.

Tested using binaries compiled with Sourcery G++ Lite 2009q1-161
and ARM RVCT 3.0.

[dbrownell@users.sourceforge.net: doc tweaks, NEWS]

Signed-off-by: Nicolas Pitre <nico@marvell.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
NEWS
doc/openocd.texi
src/target/Makefile.am
src/target/arm7_9_common.c
src/target/arm_semihosting.c [new file with mode: 0644]
src/target/arm_semihosting.h [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 1af13311b52663fe517dd6bd27b514b17aad07de..38ae4da6211ff815fad7cc587a9984cb662a62f4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,7 @@ Target Layer:
                - register names use "sp" not "r13"
                - add top-level "mcr" and "mrc" commands, replacing
                  various core-specific operations
+               - basic semihosting support
        ARM11
                - Preliminary ETM and ETB hookup
                - accelerated "flash erase_check"
index 7e232117cdb10af4eba4e5975ecb7ae8ef0f0345..4d13f073cfa280cd5ddf40a38c58f8de452fecd4 100644 (file)
@@ -5674,6 +5674,17 @@ cables (FT2232), but might be unsafe if used with targets running at very low
 speeds, like the 32kHz startup clock of an AT91RM9200.
 @end deffn
 
+@deffn Command {arm7_9 semihosting} [@option{enable}|@option{disable}]
+Display status of semihosting, after optionally changing that status.
+
+Semihosting allows for code executing on an ARM target to use the
+I/O facilities on the host computer i.e. the system where OpenOCD
+is running. The target application must be linked against a library
+implementing the ARM semihosting convention that forwards operation
+requests by using a special SVC instruction that is trapped at the
+Supervisor Call vector by OpenOCD.
+@end deffn
+
 @subsection ARM720T specific commands
 @cindex ARM720T
 
index 7631beaadd19928ea63c82532d16f05a8ae8c0b1..bd7bf7ae252b66aeb6fb8a11c3309d7fc4bb2e44 100644 (file)
@@ -78,6 +78,7 @@ ARM_DEBUG_SRC = \
        arm_jtag.c \
        arm_disassembler.c \
        arm_simulator.c \
+       arm_semihosting.c \
        arm_adi_v5.c \
        embeddedice.c \
        trace.c \
@@ -101,6 +102,7 @@ noinst_HEADERS = \
        arm_adi_v5.h \
        arm_disassembler.h \
        arm_simulator.h \
+       arm_semihosting.h \
        arm7_9_common.h \
        arm7tdmi.h \
        arm720t.h \
index 255a85f57f8207e3406e3cf68d3eb4549af71531..7318b5f36867a8332af2e7cf8d86e36d4ccb9c9e 100644 (file)
@@ -36,6 +36,7 @@
 #include "etm.h"
 #include <helper/time_support.h>
 #include "arm_simulator.h"
+#include "arm_semihosting.h"
 #include "algorithm.h"
 #include "register.h"
 
@@ -915,6 +916,9 @@ int arm7_9_poll(struct target *target)
                                }
                        }
 
+                       if (arm_semihosting(target, &retval) != 0)
+                               return retval;
+
                        if ((retval = target_call_event_callbacks(target, TARGET_EVENT_HALTED)) != ERROR_OK)
                        {
                                return retval;
@@ -2814,6 +2818,39 @@ COMMAND_HANDLER(handle_arm7_9_dcc_downloads_command)
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(handle_arm7_9_semihosting_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       struct arm7_9_common *arm7_9 = target_to_arm7_9(target);
+
+       if (!is_arm7_9(arm7_9))
+       {
+               command_print(CMD_CTX, "current target isn't an ARM7/ARM9 target");
+               return ERROR_TARGET_INVALID;
+       }
+
+       if (CMD_ARGC > 0)
+       {
+               COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting_active);
+
+               /* TODO: support other methods if vector catch is unavailable */
+               if (arm7_9->has_vector_catch) {
+                       struct reg *vector_catch = &arm7_9->eice_cache->reg_list[EICE_VEC_CATCH];
+                       if (!vector_catch->valid)
+                               embeddedice_read_reg(vector_catch);
+                       buf_set_u32(vector_catch->value, 2, 1, semihosting_active);
+                       embeddedice_store_reg(vector_catch);
+               } else if (semihosting_active) {
+                       command_print(CMD_CTX, "vector catch unavailable");
+                       semihosting_active = 0;
+               }
+       }
+
+       command_print(CMD_CTX, "semihosting is %s", (semihosting_active) ? "enabled" : "disabled");
+
+       return ERROR_OK;
+}
+
 int arm7_9_init_arch_info(struct target *target, struct arm7_9_common *arm7_9)
 {
        int retval = ERROR_OK;
@@ -2867,6 +2904,13 @@ static const struct command_registration arm7_9_any_command_handlers[] = {
                .usage = "<enable | disable>",
                .help = "use DCC downloads for larger memory writes",
        },
+       {
+               "semihosting",
+               .handler = &handle_arm7_9_semihosting_command,
+               .mode = COMMAND_EXEC,
+               .usage = "<enable | disable>",
+               .help = "activate support for semihosting operations",
+       },
        COMMAND_REGISTRATION_DONE
 };
 const struct command_registration arm7_9_command_handlers[] = {
diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c
new file mode 100644 (file)
index 0000000..5e0a2be
--- /dev/null
@@ -0,0 +1,442 @@
+/***************************************************************************
+ *   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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+/**
+ * @file
+ * Hold ARM semihosting support.
+ *
+ * Semihosting enables code running on an ARM target to use the I/O
+ * facilities on the host computer. The target application must be linked
+ * against a library that forwards operation requests by using the SVC
+ * instruction trapped at the Supervisor Call vector by the debugger.
+ * Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
+ * from ARM Ltd.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "armv4_5.h"
+#include "register.h"
+#include "arm_semihosting.h"
+#include <helper/binarybuffer.h>
+#include <helper/log.h>
+
+/* TODO: this needs to be per target */
+int semihosting_active;
+int semihosting_errno;
+
+static int do_semihosting(struct target *target)
+{
+       struct arm *armv4_5 = target_to_armv4_5(target);
+       uint32_t r0 = buf_get_u32(armv4_5->core_cache->reg_list[0].value, 0, 32);
+       uint32_t r1 = buf_get_u32(armv4_5->core_cache->reg_list[1].value, 0, 32);
+       uint32_t lr = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, ARMV4_5_MODE_SVC, 14).value, 0, 32);
+       uint32_t spsr = buf_get_u32(armv4_5->spsr->value, 0, 32);;
+       uint8_t params[16];
+       int retval, result;
+
+       /*
+        * 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.
+        */
+       switch (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);
+                       if (l <= 255 && m <= 11) {
+                               uint8_t fn[256];
+                               int mode;
+                               retval = target_read_memory(target, a, 1, l, fn);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               fn[l] = 0;
+                               if (m & 0x2)
+                                       mode = O_RDWR;
+                               else if (m & 0xc)
+                                       mode = O_WRONLY;
+                               else
+                                       mode = O_RDONLY;
+                               if (m >= 8)
+                                       mode |= O_CREAT|O_APPEND;
+                               else if (m >= 4)
+                                       mode |= O_CREAT|O_TRUNC;
+                               if (strcmp((char *)fn, ":tt") == 0) {
+                                       if ((mode & 3) == 0)
+                                               result = dup(0);
+                                       else
+                                               result = dup(1);
+                               } else
+                                       result = open((char *)fn, mode);
+                               semihosting_errno =  errno;
+                       } else {
+                               result = -1;
+                               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);
+                       result = close(fd);
+                       semihosting_errno = errno;
+               }
+               break;
+
+       case 0x03:      /* SYS_WRITEC */
+               {
+                       unsigned char c;
+                       retval = target_read_memory(target, r1, 1, 1, &c);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       putchar(c);
+                       result = 0;
+               }
+               break;
+
+       case 0x04:      /* SYS_WRITE0 */
+               do {
+                       unsigned char c;
+                       retval = target_read_memory(target, r1, 1, 1, &c);
+                       if (retval != ERROR_OK)
+                               return retval;
+                       if (!c)
+                               break;
+                       putchar(c);
+               } while (1);
+               result = 0;
+               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);
+                       uint8_t *buf = malloc(l);
+                       if (!buf) {
+                               result = -1;
+                               semihosting_errno = ENOMEM;
+                       } else {
+                               retval = target_read_buffer(target, a, l, buf);
+                               if (retval != ERROR_OK) {
+                                       free(buf);
+                                       return retval;
+                               }
+                               result = write(fd, buf, l);
+                               semihosting_errno = errno;
+                               if (result >= 0)
+                                       result = l - 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);
+                       uint8_t *buf = malloc(l);
+                       if (!buf) {
+                               result = -1;
+                               semihosting_errno = ENOMEM;
+                       } else {
+                               result = read(fd, buf, l);
+                               semihosting_errno = errno;
+                               if (result > 0) {
+                                       retval = target_write_buffer(target, a, result, buf);
+                                       if (retval != ERROR_OK) {
+                                               free(buf);
+                                               return retval;
+                                       }
+                                       result = l - result;
+                               }
+                               free(buf);
+                       }
+               }
+               break;
+
+       case 0x07:      /* SYS_READC */
+               result = getchar();
+               break;
+
+       case 0x08:      /* SYS_ISERROR */
+               retval = target_read_memory(target, r1, 4, 1, params);
+               if (retval != ERROR_OK)
+                       return retval;
+               result = (target_buffer_get_u32(target, params+0) != 0);
+               break;
+
+       case 0x09:      /* SYS_ISTTY */
+               retval = target_read_memory(target, r1, 4, 1, params);
+               if (retval != ERROR_OK)
+                       return retval;
+               result = isatty(target_buffer_get_u32(target, params+0));
+               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);
+                       result = lseek(fd, pos, SEEK_SET);
+                       semihosting_errno = errno;
+                       if (result == pos)
+                               result = 0;
+               }
+               break;
+
+       case 0x0c:      /* SYS_FLEN */
+               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);
+                       off_t cur = lseek(fd, 0, SEEK_CUR);
+                       if (cur == (off_t)-1) {
+                               semihosting_errno = errno;
+                               result = -1;
+                               break;
+                       }
+                       result = lseek(fd, 0, SEEK_END);
+                       semihosting_errno = errno;
+                       if (lseek(fd, cur, SEEK_SET) == (off_t)-1) {
+                               semihosting_errno = errno;
+                               result = -1;
+                       }
+               }
+               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 (l <= 255) {
+                               uint8_t fn[256];
+                               retval = target_read_memory(target, a, 1, l, fn);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               fn[l] = 0;
+                               result = remove((char *)fn);
+                               semihosting_errno =  errno;
+                       } else {
+                               result = -1;
+                               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 (l1 <= 255 && l2 <= 255) {
+                               uint8_t fn1[256], fn2[256];
+                               retval = target_read_memory(target, a1, 1, l1, fn1);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               retval = target_read_memory(target, a2, 1, l2, fn2);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               fn1[l1] = 0;
+                               fn2[l2] = 0;
+                               result = rename((char *)fn1, (char *)fn2);
+                               semihosting_errno =  errno;
+                       } else {
+                               result = -1;
+                               semihosting_errno = EINVAL;
+                       }
+               }
+               break;
+
+       case 0x11:      /* SYS_TIME */
+               result = time(NULL);
+               break;
+
+       case 0x13:      /* SYS_ERRNO */
+               result = 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 = "foobar";
+                       uint32_t s = strlen(arg) + 1;
+                       if (l < s)
+                               result = -1;
+                       else {
+                               retval = target_write_buffer(target, a, s, (void*)arg);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               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;
+                       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", r1);
+               }
+               return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+
+       case 0x0d:      /* SYS_TMPNAM */
+       case 0x10:      /* SYS_CLOCK */
+       case 0x12:      /* SYS_SYSTEM */
+       case 0x17:      /* angel_SWIreason_EnterSVC */
+       case 0x30:      /* SYS_ELAPSED */
+       case 0x31:      /* SYS_TICKFREQ */
+       default:
+               fprintf(stderr, "semihosting: unsupported call %#x\n", r0);
+               result = -1;
+               semihosting_errno = ENOTSUP;
+       }
+
+       /* resume execution to the original mode */
+       buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, result);
+       armv4_5->core_cache->reg_list[0].dirty = 1;
+       buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, lr);
+       armv4_5->core_cache->reg_list[15].dirty = 1;
+       buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, spsr);
+       armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1;
+       armv4_5->core_mode = spsr & 0x1f;
+       if (spsr & 0x20)
+               armv4_5->core_state = ARMV4_5_STATE_THUMB;
+       return target_resume(target, 1, 0, 0, 0);
+}
+
+/**
+ * Checks for and processes an ARM semihosting request.  This is meant
+ * to be called when the target is stopped due to a debug mode entry.
+ * If the value 0 is returned then there was nothing to process. A non-zero
+ * return value signifies that a request was processed and the target resumed,
+ * or an error was encountered, in which case the caller must return
+ * immediately.
+ *
+ * @param target Pointer to the ARM target to process
+ * @param retval Pointer to a location where the return code will be stored
+ * @return non-zero value if a request was processed or an error encountered
+ */
+int arm_semihosting(struct target *target, int *retval)
+{
+       struct arm *armv4_5 = target_to_armv4_5(target);
+       uint32_t lr, spsr;
+
+       if (!semihosting_active ||
+           armv4_5->core_mode != ARMV4_5_MODE_SVC ||
+           buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32) != 0x08)
+               return 0;
+
+       lr = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, ARMV4_5_MODE_SVC, 14).value, 0, 32);
+       spsr = buf_get_u32(armv4_5->spsr->value, 0, 32);
+
+       /* check instruction that triggered this trap */
+       if (spsr & (1 << 5)) {
+               /* was in Thumb mode */
+               uint8_t insn_buf[2];
+               uint16_t insn;
+               *retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
+               if (*retval != ERROR_OK)
+                       return 1;
+               insn = target_buffer_get_u16(target, insn_buf);
+               if (insn != 0xDFAB)
+                       return 0;
+       } else {
+               /* was in ARM mode */
+               uint8_t insn_buf[4];
+               uint32_t insn;
+               *retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
+               if (*retval != ERROR_OK)
+                       return 1;
+               insn = target_buffer_get_u32(target, insn_buf);
+               if (insn != 0xEF123456)
+                       return 0;
+       }
+
+       *retval = do_semihosting(target);
+       return 1;
+}
diff --git a/src/target/arm_semihosting.h b/src/target/arm_semihosting.h
new file mode 100644 (file)
index 0000000..6b9ac56
--- /dev/null
@@ -0,0 +1,28 @@
+/***************************************************************************
+ *   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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef ARM_SEMIHOSTING_H
+#define ARM_SEMIHOSTING_H
+
+extern int semihosting_active;
+
+int arm_semihosting(struct target *target, int *retval);
+
+#endif