This is an automated email from Gerrit. Liviu Ionescu ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/4518
-- gerrit commit 4ccd4b7cfb3bc8b7857fca7493d7f3f8b672ccfb Author: Liviu Ionescu <[email protected]> Date: Sun May 13 18:39:06 2018 +0300 Rework/update ARM semihosting In 2016, ARM released the second edition of the semihosting specs ("Semihosting for AArch32 and AArch64"), adding support for 64-bits. To ease the reuse of semihosting for other platforms (like RISC-V), the semihosting code was isolated from the ARM target and updated to the latest specs. Signed-off-by: Liviu Ionescu <[email protected]> Change-Id: Ie84dbd86a547323bb8a5d24eab68fc7dad013d96 diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 206b281..0122655 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 = \ diff --git a/src/target/semihosting_common.c b/src/target/semihosting_common.c new file mode 100644 index 0000000..0ba8e47 --- /dev/null +++ b/src/target/semihosting_common.c @@ -0,0 +1,1567 @@ +/*************************************************************************** + * Copyright (C) 2018 by Liviu Ionescu * + * <[email protected]> * + * * + * Copyright (C) 2018 by Marvell Technology Group Ltd. * + * Written by Nicolas Pitre <[email protected]> * + * * + * Copyright (C) 2010 by Spencer Oliver * + * [email protected] * + * * + * Copyright (C) 2016 by Square, Inc. * + * Steven Stallion <[email protected]> * + * * + * 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. + */ +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_arm_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_arm_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 (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_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 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_arm_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 (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..e1f2fe1 --- /dev/null +++ b/src/target/semihosting_common.h @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (C) 2018 by Liviu Ionescu * + * <[email protected]> * + * * + * Copyright (C) 2009 by Marvell Technology Group Ltd. * + * Written by Nicolas Pitre <[email protected]> * + * * + * 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 exit is called during a debug session, execution anyway + * returns to the debugger, execution halting to the BRK. + * The standard allows for exit to return, but the condition + * to trigger this is a bit obscure ("by performing an RDI_Execute + * request or equivalent"). + * To make the exit call return, enable this. + */ + bool has_resumable_exit; + + /** The Target (hart) word size; 8 for 64-bits targets. */ + size_t word_size_bytes; + + /** The current semihosting operation (R0). */ + int op; + + /** The current semihosting parameter (R1). */ + 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 */ -- ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
