semihosting: User defined operation, Tcl command exec on host
authorZoltán Dudás <zedudi@gmail.com>
Wed, 24 Nov 2021 16:13:50 +0000 (17:13 +0100)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 5 Feb 2022 21:40:17 +0000 (21:40 +0000)
Enabling a portion (0x100 - 0x107) of the user defined semihosting
operation number range (0x100 - 0x1FF) to be processed with the help of
the existing target event mechanism, to implement a general-purpose Tcl
interface for the target available on the host, via semihosting
interface.

Example usage:
- The user configures a Tcl command as a callback for one of the newly
defined events (semihosting-user-cmd-0x10X) in the configuration
file.
- The target can make a semihosting call with <opnum>, passing optional
parameters for the call.

If there is no callback registered to the user defined operation number,
nothing happens.

Example usage: Configure RTT automatically with the exact, linked
control block location from target.

Signed-off-by: Zoltán Dudás <zedudi@gmail.com>
Change-Id: I10e1784b1fecd4e630d78df81cb44bf1aa2fc247
Reviewed-on: https://review.openocd.org/c/openocd/+/6748
Tested-by: jenkins
Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
doc/openocd.texi
src/target/arm_semihosting.c
src/target/riscv/riscv_semihosting.c
src/target/semihosting_common.c
src/target/semihosting_common.h
src/target/target.c
src/target/target.h

index ed92eb4ec5bff5b2ca17e5e8d4d074de82eda9a3..e2c4954733a236a150752533eba18f55a07b898c 100644 (file)
@@ -5185,6 +5185,22 @@ when reset disables PLLs needed to use a fast clock.
 @* After single-step has completed
 @item @b{trace-config}
 @* After target hardware trace configuration was changed
+@item @b{semihosting-user-cmd-0x100}
+@* The target made a semihosting call with user-defined operation number 0x100
+@item @b{semihosting-user-cmd-0x101}
+@* The target made a semihosting call with user-defined operation number 0x101
+@item @b{semihosting-user-cmd-0x102}
+@* The target made a semihosting call with user-defined operation number 0x102
+@item @b{semihosting-user-cmd-0x103}
+@* The target made a semihosting call with user-defined operation number 0x103
+@item @b{semihosting-user-cmd-0x104}
+@* The target made a semihosting call with user-defined operation number 0x104
+@item @b{semihosting-user-cmd-0x105}
+@* The target made a semihosting call with user-defined operation number 0x105
+@item @b{semihosting-user-cmd-0x106}
+@* The target made a semihosting call with user-defined operation number 0x106
+@item @b{semihosting-user-cmd-0x107}
+@* The target made a semihosting call with user-defined operation number 0x107
 @end itemize
 
 @quotation Note
@@ -9241,6 +9257,17 @@ To make the SEMIHOSTING_SYS_EXIT call return normally, enable
 this option (default: disabled).
 @end deffn
 
+@deffn {Command} {arm semihosting_read_user_param}
+@cindex ARM semihosting
+Read parameter of the semihosting call from the target. Usable in
+semihosting-user-cmd-0x10* event handlers, returning a string.
+
+When the target makes semihosting call with operation number from range 0x100-
+0x107, an optional string parameter can be passed to the server. This parameter
+is valid during the run of the event handlers and is accessible with this
+command.
+@end deffn
+
 @section ARMv4 and ARMv5 Architecture
 @cindex ARMv4
 @cindex ARMv5
index 792474acf0de7eb66a5b3cadce4f20ddaf45f2bf..507d1cd2c4580f80687288132326cf5582e39c31 100644 (file)
@@ -367,10 +367,13 @@ int arm_semihosting(struct target *target, int *retval)
                }
 
                /* Check for ARM operation numbers. */
-               if (semihosting->op >= 0 && semihosting->op <= 0x31) {
+               if ((semihosting->op >= 0 && semihosting->op <= 0x31) ||
+                       (semihosting->op >= 0x100 && semihosting->op <= 0x107)) {
+
                        *retval = semihosting_common(target);
                        if (*retval != ERROR_OK) {
-                               LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
+                               LOG_ERROR("Failed semihosting operation (0x%02X)",
+                                               semihosting->op);
                                return 0;
                        }
                } else {
index b347212d3417f9e2fd336788f2b0c17ced310a86..1dd8e7791a5e5cc66ca75a298559fa9648d66870 100644 (file)
@@ -140,7 +140,9 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval)
                semihosting->word_size_bytes = riscv_xlen(target) / 8;
 
                /* Check for ARM operation numbers. */
-               if (semihosting->op >= 0 && semihosting->op <= 0x31) {
+               if ((semihosting->op >= 0 && semihosting->op <= 0x31) ||
+                       (semihosting->op >= 0x100 && semihosting->op <= 0x107)) {
+
                        *retval = semihosting_common(target);
                        if (*retval != ERROR_OK) {
                                LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
index 8d3f66ca530e528b5a5803d1e687d6a7eccdf373..9e60de5722123955c4ac7456adaa2ff15d3c17fa 100644 (file)
@@ -154,6 +154,12 @@ int semihosting_common_init(struct target *target, void *setup,
        return ERROR_OK;
 }
 
+/**
+ * User operation parameter string storage buffer. Contains valid data when the
+ * TARGET_EVENT_SEMIHOSTING_USER_CMD_xxxxx event callbacks are running.
+ */
+static char *semihosting_user_op_params;
+
 /**
  * Portable implementation of ARM semihosting calls.
  * Performs the currently pending semihosting operation
@@ -183,7 +189,7 @@ int semihosting_common(struct target *target)
        /* Enough space to hold 4 long words. */
        uint8_t fields[4*8];
 
-       LOG_DEBUG("op=0x%x, param=0x%" PRIx64, (int)semihosting->op,
+       LOG_DEBUG("op=0x%x, param=0x%" PRIx64, semihosting->op,
                semihosting->param);
 
        switch (semihosting->op) {
@@ -1278,6 +1284,71 @@ int semihosting_common(struct target *target)
                        }
                        break;
 
+               case SEMIHOSTING_USER_CMD_0x100 ... SEMIHOSTING_USER_CMD_0x107:
+                       /**
+                        * This is a user defined operation (while user cmds 0x100-0x1ff
+                        * are possible, only 0x100-0x107 are currently implemented).
+                        *
+                        * Reads the user operation parameters from target, then fires the
+                        * corresponding target event. When the target callbacks returned,
+                        * cleans up the command parameter buffer.
+                        *
+                        * Entry
+                        * On entry, the PARAMETER REGISTER contains a pointer to a
+                        * two-field data block:
+                        * - field 1 Contains a pointer to the bound command parameter
+                        * string
+                        * - field 2 Contains the command parameter string length
+                        *
+                        * Return
+                        * On exit, the RETURN REGISTER contains the return status.
+                        */
+               {
+                       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"
+                                               " op=0x%x", semihosting->op);
+                               return retval;
+                       }
+
+                       uint64_t addr = semihosting_get_field(target, 0, fields);
+
+                       size_t len = semihosting_get_field(target, 1, fields);
+                       if (len > SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH) {
+                               LOG_ERROR("The maximum length for user defined command "
+                                               "parameter is %u, received length is %zu (op=0x%x)",
+                                               SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH,
+                                               len,
+                                               semihosting->op);
+                               return ERROR_FAIL;
+                       }
+
+                       semihosting_user_op_params = malloc(len + 1);
+                       if (!semihosting_user_op_params)
+                               return ERROR_FAIL;
+                       semihosting_user_op_params[len] = 0;
+
+                       retval = target_read_buffer(target, addr, len,
+                                       (uint8_t *)(semihosting_user_op_params));
+                       if (retval != ERROR_OK) {
+                               LOG_ERROR("Failed to read from target, semihosting op=0x%x",
+                                               semihosting->op);
+                               free(semihosting_user_op_params);
+                               semihosting_user_op_params = NULL;
+                               return retval;
+                       }
+
+                       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 */
                /*
                 * Returns the number of elapsed target ticks since execution
@@ -1624,6 +1695,30 @@ COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command)
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(handle_common_semihosting_read_user_param_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       struct semihosting *semihosting = target->semihosting;
+
+       if (CMD_ARGC)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (!semihosting->is_active) {
+               LOG_ERROR("semihosting not yet enabled for current target");
+               return ERROR_FAIL;
+       }
+
+       if (!semihosting_user_op_params) {
+               LOG_ERROR("This command is usable only from a registered user "
+                               "semihosting event callback.");
+               return ERROR_FAIL;
+       }
+
+       command_print_sameline(CMD, "%s", semihosting_user_op_params);
+
+       return ERROR_OK;
+}
+
 const struct command_registration semihosting_common_handlers[] = {
        {
                "semihosting",
@@ -1653,5 +1748,12 @@ const struct command_registration semihosting_common_handlers[] = {
                .usage = "['enable'|'disable']",
                .help = "activate support for semihosting resumable exit",
        },
+       {
+               "semihosting_read_user_param",
+               .handler = handle_common_semihosting_read_user_param_command,
+               .mode = COMMAND_EXEC,
+               .usage = "",
+               .help = "read parameters in semihosting-user-cmd-0x10X callbacks",
+       },
        COMMAND_REGISTRATION_DONE
 };
index b83464ed5a49a19915f49905c1204089fec18e9b..6eb9ca2527aee52d12e3d7ae1f19e9e2377a5f0d 100644 (file)
@@ -75,8 +75,14 @@ enum semihosting_operation_numbers {
        SEMIHOSTING_SYS_WRITE = 0x05,
        SEMIHOSTING_SYS_WRITEC = 0x03,
        SEMIHOSTING_SYS_WRITE0 = 0x04,
+       SEMIHOSTING_USER_CMD_0x100 = 0x100, /* First user cmd op code */
+       SEMIHOSTING_USER_CMD_0x107 = 0x107, /* Last supported user cmd op code */
+       SEMIHOSTING_USER_CMD_0x1FF = 0x1FF, /* Last user cmd op code */
 };
 
+/** Maximum allowed Tcl command segment length in bytes*/
+#define SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH (1024 * 1024)
+
 /*
  * Codes used by SEMIHOSTING_SYS_EXIT (formerly
  * SEMIHOSTING_REPORT_EXCEPTION).
index 7cdd8d83029c825391303aa0d8f2bddbd9a480a6..acfbd3df5c85a0e1e56e182d9e2e27dcff6e98e4 100644 (file)
@@ -236,6 +236,15 @@ static const struct jim_nvp nvp_target_event[] = {
 
        { .value = TARGET_EVENT_TRACE_CONFIG, .name = "trace-config" },
 
+       { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x100, .name = "semihosting-user-cmd-0x100" },
+       { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x101, .name = "semihosting-user-cmd-0x101" },
+       { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x102, .name = "semihosting-user-cmd-0x102" },
+       { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x103, .name = "semihosting-user-cmd-0x103" },
+       { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x104, .name = "semihosting-user-cmd-0x104" },
+       { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x105, .name = "semihosting-user-cmd-0x105" },
+       { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x106, .name = "semihosting-user-cmd-0x106" },
+       { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x107, .name = "semihosting-user-cmd-0x107" },
+
        { .name = NULL, .value = -1 }
 };
 
index 6ef8f10e0719396b027cd2f50edd9e9a0b41c3d0..974630f178555016dbbdf4c683d634f2b08067ae 100644 (file)
@@ -294,6 +294,15 @@ enum target_event {
        TARGET_EVENT_GDB_FLASH_WRITE_END,
 
        TARGET_EVENT_TRACE_CONFIG,
+
+       TARGET_EVENT_SEMIHOSTING_USER_CMD_0x100 = 0x100, /* semihosting allows user cmds from 0x100 to 0x1ff */
+       TARGET_EVENT_SEMIHOSTING_USER_CMD_0x101 = 0x101,
+       TARGET_EVENT_SEMIHOSTING_USER_CMD_0x102 = 0x102,
+       TARGET_EVENT_SEMIHOSTING_USER_CMD_0x103 = 0x103,
+       TARGET_EVENT_SEMIHOSTING_USER_CMD_0x104 = 0x104,
+       TARGET_EVENT_SEMIHOSTING_USER_CMD_0x105 = 0x105,
+       TARGET_EVENT_SEMIHOSTING_USER_CMD_0x106 = 0x106,
+       TARGET_EVENT_SEMIHOSTING_USER_CMD_0x107 = 0x107,
 };
 
 struct target_event_action {