#include <helper/log.h>
#include <sys/stat.h>
+/**
+ * It is not possible to use O_... flags defined in sys/stat.h because they
+ * are not guaranteed to match the values defined by the GDB Remote Protocol.
+ * See https://sourceware.org/gdb/onlinedocs/gdb/Open-Flags.html#Open-Flags
+ */
+enum {
+ TARGET_O_RDONLY = 0x000,
+ TARGET_O_WRONLY = 0x001,
+ TARGET_O_RDWR = 0x002,
+ TARGET_O_APPEND = 0x008,
+ TARGET_O_CREAT = 0x200,
+ TARGET_O_TRUNC = 0x400,
+ /* O_EXCL=0x800 is not required in this implementation. */
+};
+
+/* GDB remote protocol does not differentiate between text and binary open modes. */
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
+ TARGET_O_RDONLY,
+ TARGET_O_RDONLY,
+ TARGET_O_RDWR,
+ TARGET_O_RDWR,
+ TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_TRUNC,
+ TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_TRUNC,
+ TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_TRUNC,
+ TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_TRUNC,
+ TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_APPEND,
+ TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_APPEND,
+ TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_APPEND,
+ TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_APPEND
};
static int semihosting_common_fileio_info(struct target *target,
LOG_DEBUG(" ");
target->fileio_info = malloc(sizeof(*target->fileio_info));
- if (target->fileio_info == NULL) {
+ if (!target->fileio_info) {
LOG_ERROR("out of memory");
return ERROR_FAIL;
}
struct semihosting *semihosting;
semihosting = malloc(sizeof(*target->semihosting));
- if (semihosting == NULL) {
+ if (!semihosting) {
LOG_ERROR("out of memory");
return ERROR_FAIL;
}
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
/* 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) {
uint64_t addr = semihosting_get_field(target, 0, fields);
size_t size = semihosting_get_field(target, 1, fields);
- char *arg = semihosting->cmdline != NULL ?
+ char *arg = semihosting->cmdline ?
semihosting->cmdline : "";
uint32_t len = strlen(arg) + 1;
if (len > size)
}
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
/* -------------------------------------------------------------------------
* Common semihosting commands handlers. */
-static __COMMAND_HANDLER(handle_common_semihosting_command)
+COMMAND_HANDLER(handle_common_semihosting_command)
{
struct target *target = get_current_target(CMD_CTX);
- if (target == NULL) {
+ if (!target) {
LOG_ERROR("No target selected");
return ERROR_FAIL;
}
return ERROR_OK;
}
-static __COMMAND_HANDLER(handle_common_semihosting_fileio_command)
+COMMAND_HANDLER(handle_common_semihosting_fileio_command)
{
struct target *target = get_current_target(CMD_CTX);
- if (target == NULL) {
+ if (!target) {
LOG_ERROR("No target selected");
return ERROR_FAIL;
}
return ERROR_OK;
}
-static __COMMAND_HANDLER(handle_common_semihosting_cmdline)
+COMMAND_HANDLER(handle_common_semihosting_cmdline)
{
struct target *target = get_current_target(CMD_CTX);
unsigned int i;
- if (target == NULL) {
+ if (!target) {
LOG_ERROR("No target selected");
return ERROR_FAIL;
}
for (i = 1; i < CMD_ARGC; i++) {
char *cmdline = alloc_printf("%s %s", semihosting->cmdline, CMD_ARGV[i]);
- if (cmdline == NULL)
+ if (!cmdline)
break;
free(semihosting->cmdline);
semihosting->cmdline = cmdline;
return ERROR_OK;
}
-static __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command)
+COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command)
{
struct target *target = get_current_target(CMD_CTX);
- if (target == NULL) {
+ if (!target) {
LOG_ERROR("No target selected");
return ERROR_FAIL;
}
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",
.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
};