openocd: src/target: replace the GPL-2.0-or-later license tag
[fw/openocd] / src / target / semihosting_common.c
index 38035e493d420c8172d86cda4323757dcb0d8515..e31ff6fd4a6fde99a3e7ae0af99adc051ab64e01 100644 (file)
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
 /***************************************************************************
  *   Copyright (C) 2018 by Liviu Ionescu                                   *
  *   <ilg@livius.net>                                                      *
  *                                                                         *
  *   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/>. *
  ***************************************************************************/
 
 /**
@@ -68,7 +57,7 @@ enum {
 };
 
 /* GDB remote protocol does not differentiate between text and binary open modes. */
-static const int open_modeflags[12] = {
+static const int open_gdb_modeflags[12] = {
        TARGET_O_RDONLY,
        TARGET_O_RDONLY,
        TARGET_O_RDWR,
@@ -83,21 +72,26 @@ static const int open_modeflags[12] = {
        TARGET_O_RDWR   | TARGET_O_CREAT | TARGET_O_APPEND
 };
 
+static const int open_host_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;
 
@@ -144,12 +138,14 @@ int semihosting_common_init(struct target *target, void *setup,
        semihosting->result = -1;
        semihosting->sys_errno = -1;
        semihosting->cmdline = NULL;
+       semihosting->basedir = NULL;
 
        /* If possible, update it in setup(). */
        semihosting->setup_time = clock();
 
        semihosting->setup = setup;
        semihosting->post_result = post_result;
+       semihosting->user_command_extension = NULL;
 
        target->semihosting = semihosting;
 
@@ -855,17 +851,23 @@ int semihosting_common(struct target *target)
                                        semihosting->sys_errno = EINVAL;
                                        break;
                                }
-                               uint8_t *fn = malloc(len+1);
+                               size_t basedir_len = semihosting->basedir ? strlen(semihosting->basedir) : 0;
+                               uint8_t *fn = malloc(basedir_len + len + 2);
                                if (!fn) {
                                        semihosting->result = -1;
                                        semihosting->sys_errno = ENOMEM;
                                } else {
-                                       retval = target_read_memory(target, addr, 1, len, fn);
+                                       if (basedir_len > 0) {
+                                               strcpy((char *)fn, semihosting->basedir);
+                                               if (fn[basedir_len - 1] != '/')
+                                                       fn[basedir_len++] = '/';
+                                       }
+                                       retval = target_read_memory(target, addr, 1, len, fn + basedir_len);
                                        if (retval != ERROR_OK) {
                                                free(fn);
                                                return retval;
                                        }
-                                       fn[len] = 0;
+                                       fn[basedir_len + len] = 0;
                                        /* TODO: implement the :semihosting-features special file.
                                         * */
                                        if (semihosting->is_fileio) {
@@ -886,7 +888,7 @@ int semihosting_common(struct target *target)
                                                        fileio_info->identifier = "open";
                                                        fileio_info->param_1 = addr;
                                                        fileio_info->param_2 = len;
-                                                       fileio_info->param_3 = open_modeflags[mode];
+                                                       fileio_info->param_3 = open_gdb_modeflags[mode];
                                                        fileio_info->param_4 = 0644;
                                                }
                                        } else {
@@ -922,7 +924,7 @@ int semihosting_common(struct target *target)
                                                         * otherwise it will fail to reopen a previously
                                                         * written file */
                                                        semihosting->result = open((char *)fn,
-                                                                       open_modeflags[mode],
+                                                                       open_host_modeflags[mode],
                                                                        0644);
                                                        semihosting->sys_errno = errno;
                                                        LOG_DEBUG("open('%s')=%d", fn,
@@ -1445,9 +1447,14 @@ int semihosting_common(struct target *target)
                         * Return
                         * On exit, the RETURN REGISTER contains the return status.
                         */
-               {
-                       assert(!semihosting_user_op_params);
+                       if (semihosting->user_command_extension) {
+                               retval = semihosting->user_command_extension(target);
+                               if (retval != ERROR_NOT_IMPLEMENTED)
+                                       break;
+                               /* If custom user command not handled, we are looking for the TCL handler */
+                       }
 
+                       assert(!semihosting_user_op_params);
                        retval = semihosting_read_fields(target, 2, fields);
                        if (retval != ERROR_OK) {
                                LOG_ERROR("Failed to read fields for user defined command"
@@ -1485,11 +1492,8 @@ int semihosting_common(struct target *target)
                        target_handle_event(target, semihosting->op);
                        free(semihosting_user_op_params);
                        semihosting_user_op_params = NULL;
-
                        semihosting->result = 0;
                        break;
-               }
-
 
                case SEMIHOSTING_SYS_ELAPSED:   /* 0x30 */
                /*
@@ -1626,17 +1630,11 @@ static int semihosting_common_fileio_end(struct target *target, int result,
         */
        switch (semihosting->op) {
                case SEMIHOSTING_SYS_WRITE:     /* 0x05 */
+               case SEMIHOSTING_SYS_READ:      /* 0x06 */
                        if (result < 0)
-                               semihosting->result = fileio_info->param_3;
+                               semihosting->result = fileio_info->param_3;  /* Zero bytes read/written. */
                        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;
+                               semihosting->result = (int64_t)fileio_info->param_3 - result;
                        break;
 
                case SEMIHOSTING_SYS_SEEK:      /* 0x0a */
@@ -1648,10 +1646,13 @@ static int semihosting_common_fileio_end(struct target *target, int result,
        return semihosting->post_result(target);
 }
 
+/* -------------------------------------------------------------------------
+ * Utility functions. */
+
 /**
  * Read all fields of a command from target to buffer.
  */
-static int semihosting_read_fields(struct target *target, size_t number,
+int semihosting_read_fields(struct target *target, size_t number,
        uint8_t *fields)
 {
        struct semihosting *semihosting = target->semihosting;
@@ -1663,7 +1664,7 @@ static int semihosting_read_fields(struct target *target, size_t number,
 /**
  * Write all fields of a command from buffer to target.
  */
-static int semihosting_write_fields(struct target *target, size_t number,
+int semihosting_write_fields(struct target *target, size_t number,
        uint8_t *fields)
 {
        struct semihosting *semihosting = target->semihosting;
@@ -1675,7 +1676,7 @@ static int semihosting_write_fields(struct target *target, size_t number,
 /**
  * Extract a field from the buffer, considering register size and endianness.
  */
-static uint64_t semihosting_get_field(struct target *target, size_t index,
+uint64_t semihosting_get_field(struct target *target, size_t index,
        uint8_t *fields)
 {
        struct semihosting *semihosting = target->semihosting;
@@ -1688,7 +1689,7 @@ static uint64_t semihosting_get_field(struct target *target, size_t index,
 /**
  * Store a field in the buffer, considering register size and endianness.
  */
-static void semihosting_set_field(struct target *target, uint64_t value,
+void semihosting_set_field(struct target *target, uint64_t value,
        size_t index,
        uint8_t *fields)
 {
@@ -1812,7 +1813,7 @@ COMMAND_HANDLER(handle_common_semihosting_redirect_command)
 {
        struct target *target = get_current_target(CMD_CTX);
 
-       if (target == NULL) {
+       if (!target) {
                LOG_ERROR("No target selected");
                return ERROR_FAIL;
        }
@@ -2010,6 +2011,44 @@ COMMAND_HANDLER(handle_common_semihosting_read_user_param_command)
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(handle_common_semihosting_basedir_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+
+       if (CMD_ARGC > 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (!target) {
+               LOG_ERROR("No target selected");
+               return ERROR_FAIL;
+       }
+
+       struct semihosting *semihosting = target->semihosting;
+       if (!semihosting) {
+               command_print(CMD, "semihosting not supported for current target");
+               return ERROR_FAIL;
+       }
+
+       if (!semihosting->is_active) {
+               command_print(CMD, "semihosting not yet enabled for current target");
+               return ERROR_FAIL;
+       }
+
+       if (CMD_ARGC > 0) {
+               free(semihosting->basedir);
+               semihosting->basedir = strdup(CMD_ARGV[0]);
+               if (!semihosting->basedir) {
+                       command_print(CMD, "semihosting failed to allocate memory for basedir!");
+                       return ERROR_FAIL;
+               }
+       }
+
+       command_print(CMD, "semihosting base dir: %s",
+               semihosting->basedir ? semihosting->basedir : "");
+
+       return ERROR_OK;
+}
+
 const struct command_registration semihosting_common_handlers[] = {
        {
                .name = "semihosting",
@@ -2053,5 +2092,12 @@ const struct command_registration semihosting_common_handlers[] = {
                .usage = "",
                .help = "read parameters in semihosting-user-cmd-0x10X callbacks",
        },
+       {
+               .name = "semihosting_basedir",
+               .handler = handle_common_semihosting_basedir_command,
+               .mode = COMMAND_EXEC,
+               .usage = "[dir]",
+               .help = "set the base directory for semihosting I/O operations",
+       },
        COMMAND_REGISTRATION_DONE
 };