semihosting: User defined operation, Tcl command exec on host

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>
This commit is contained in:
Zoltán Dudás
2021-11-24 17:13:50 +01:00
committed by Antonio Borneo
parent dbbac5f11d
commit 5ab74bde06
7 changed files with 162 additions and 4 deletions

View 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
};