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:
parent
dbbac5f11d
commit
5ab74bde06
|
@ -5185,6 +5185,22 @@ when reset disables PLLs needed to use a fast clock.
|
||||||
@* After single-step has completed
|
@* After single-step has completed
|
||||||
@item @b{trace-config}
|
@item @b{trace-config}
|
||||||
@* After target hardware trace configuration was changed
|
@* 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
|
@end itemize
|
||||||
|
|
||||||
@quotation Note
|
@quotation Note
|
||||||
|
@ -9241,6 +9257,17 @@ To make the SEMIHOSTING_SYS_EXIT call return normally, enable
|
||||||
this option (default: disabled).
|
this option (default: disabled).
|
||||||
@end deffn
|
@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
|
@section ARMv4 and ARMv5 Architecture
|
||||||
@cindex ARMv4
|
@cindex ARMv4
|
||||||
@cindex ARMv5
|
@cindex ARMv5
|
||||||
|
|
|
@ -367,10 +367,13 @@ int arm_semihosting(struct target *target, int *retval)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for ARM operation numbers. */
|
/* 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);
|
*retval = semihosting_common(target);
|
||||||
if (*retval != ERROR_OK) {
|
if (*retval != ERROR_OK) {
|
||||||
LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
|
LOG_ERROR("Failed semihosting operation (0x%02X)",
|
||||||
|
semihosting->op);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -140,7 +140,9 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval)
|
||||||
semihosting->word_size_bytes = riscv_xlen(target) / 8;
|
semihosting->word_size_bytes = riscv_xlen(target) / 8;
|
||||||
|
|
||||||
/* Check for ARM operation numbers. */
|
/* 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);
|
*retval = semihosting_common(target);
|
||||||
if (*retval != ERROR_OK) {
|
if (*retval != ERROR_OK) {
|
||||||
LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
|
LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
|
||||||
|
|
|
@ -154,6 +154,12 @@ int semihosting_common_init(struct target *target, void *setup,
|
||||||
return ERROR_OK;
|
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.
|
* Portable implementation of ARM semihosting calls.
|
||||||
* Performs the currently pending semihosting operation
|
* Performs the currently pending semihosting operation
|
||||||
|
@ -183,7 +189,7 @@ int semihosting_common(struct target *target)
|
||||||
/* Enough space to hold 4 long words. */
|
/* Enough space to hold 4 long words. */
|
||||||
uint8_t fields[4*8];
|
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);
|
semihosting->param);
|
||||||
|
|
||||||
switch (semihosting->op) {
|
switch (semihosting->op) {
|
||||||
|
@ -1278,6 +1284,71 @@ int semihosting_common(struct target *target)
|
||||||
}
|
}
|
||||||
break;
|
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 */
|
case SEMIHOSTING_SYS_ELAPSED: /* 0x30 */
|
||||||
/*
|
/*
|
||||||
* Returns the number of elapsed target ticks since execution
|
* Returns the number of elapsed target ticks since execution
|
||||||
|
@ -1624,6 +1695,30 @@ COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command)
|
||||||
return ERROR_OK;
|
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[] = {
|
const struct command_registration semihosting_common_handlers[] = {
|
||||||
{
|
{
|
||||||
"semihosting",
|
"semihosting",
|
||||||
|
@ -1653,5 +1748,12 @@ const struct command_registration semihosting_common_handlers[] = {
|
||||||
.usage = "['enable'|'disable']",
|
.usage = "['enable'|'disable']",
|
||||||
.help = "activate support for semihosting resumable exit",
|
.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
|
COMMAND_REGISTRATION_DONE
|
||||||
};
|
};
|
||||||
|
|
|
@ -75,8 +75,14 @@ enum semihosting_operation_numbers {
|
||||||
SEMIHOSTING_SYS_WRITE = 0x05,
|
SEMIHOSTING_SYS_WRITE = 0x05,
|
||||||
SEMIHOSTING_SYS_WRITEC = 0x03,
|
SEMIHOSTING_SYS_WRITEC = 0x03,
|
||||||
SEMIHOSTING_SYS_WRITE0 = 0x04,
|
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
|
* Codes used by SEMIHOSTING_SYS_EXIT (formerly
|
||||||
* SEMIHOSTING_REPORT_EXCEPTION).
|
* SEMIHOSTING_REPORT_EXCEPTION).
|
||||||
|
|
|
@ -236,6 +236,15 @@ static const struct jim_nvp nvp_target_event[] = {
|
||||||
|
|
||||||
{ .value = TARGET_EVENT_TRACE_CONFIG, .name = "trace-config" },
|
{ .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 }
|
{ .name = NULL, .value = -1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -294,6 +294,15 @@ enum target_event {
|
||||||
TARGET_EVENT_GDB_FLASH_WRITE_END,
|
TARGET_EVENT_GDB_FLASH_WRITE_END,
|
||||||
|
|
||||||
TARGET_EVENT_TRACE_CONFIG,
|
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 {
|
struct target_event_action {
|
||||||
|
|
Loading…
Reference in New Issue