Rework/update ARM semihosting
In 2016, ARM released the second edition of the semihosting specs ("Semihosting for AArch32 and AArch64"), adding support for 64-bits. To ease the reuse of the semihosting logic for other platforms (like RISC-V), the semihosting code was isolated from the ARM target and updated to the latest specs. The new code is already in use since January (in GNU MCU Eclipse OpenOCD) and no problems were reported, neither for ARM nor for RISC-V targets, after more than 7K downloads. The 2 new files were formatted with uncrustify. Change-Id: Ie84dbd86a547323bb8a5d24eab68fc7dad013d96 Signed-off-by: Liviu Ionescu <ilg@livius.net> Reviewed-on: http://openocd.zylin.com/4518 Tested-by: jenkins Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
This commit is contained in:
parent
cdf1e826eb
commit
2517bae6c1
|
@ -8092,6 +8092,30 @@ interacting with remote files or displaying console messages in the
|
|||
debugger.
|
||||
@end deffn
|
||||
|
||||
@deffn Command {arm semihosting_resexit} [@option{enable}|@option{disable}]
|
||||
@cindex ARM semihosting
|
||||
Enable resumable SEMIHOSTING_SYS_EXIT.
|
||||
|
||||
When SEMIHOSTING_SYS_EXIT is called outside a debug session,
|
||||
things are simple, the openocd process calls exit() and passes
|
||||
the value returned by the target.
|
||||
|
||||
When SEMIHOSTING_SYS_EXIT is called during a debug session,
|
||||
by default execution returns to the debugger, leaving the
|
||||
debugger in a HALT state, similar to the state entered when
|
||||
encountering a break.
|
||||
|
||||
In some use cases, it is useful to have SEMIHOSTING_SYS_EXIT
|
||||
return normally, as any semihosting call, and do not break
|
||||
to the debugger.
|
||||
The standard allows this to happen, but the condition
|
||||
to trigger it is a bit obscure ("by performing an RDI_Execute
|
||||
request or equivalent").
|
||||
|
||||
To make the SEMIHOSTING_SYS_EXIT call return normally, enable
|
||||
this option (default: disabled).
|
||||
@end deffn
|
||||
|
||||
@section ARMv4 and ARMv5 Architecture
|
||||
@cindex ARMv4
|
||||
@cindex ARMv5
|
||||
|
|
|
@ -793,64 +793,64 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio
|
|||
bool program_exited = false;
|
||||
|
||||
if (strcmp(target->fileio_info->identifier, "open") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3,
|
||||
target->fileio_info->param_4);
|
||||
else if (strcmp(target->fileio_info->identifier, "close") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1);
|
||||
else if (strcmp(target->fileio_info->identifier, "read") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3);
|
||||
else if (strcmp(target->fileio_info->identifier, "write") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3);
|
||||
else if (strcmp(target->fileio_info->identifier, "lseek") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3);
|
||||
else if (strcmp(target->fileio_info->identifier, "rename") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3,
|
||||
target->fileio_info->param_4);
|
||||
else if (strcmp(target->fileio_info->identifier, "unlink") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2);
|
||||
else if (strcmp(target->fileio_info->identifier, "stat") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3);
|
||||
else if (strcmp(target->fileio_info->identifier, "fstat") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2);
|
||||
else if (strcmp(target->fileio_info->identifier, "gettimeofday") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2);
|
||||
else if (strcmp(target->fileio_info->identifier, "isatty") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1);
|
||||
else if (strcmp(target->fileio_info->identifier, "system") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2);
|
||||
else if (strcmp(target->fileio_info->identifier, "exit") == 0) {
|
||||
/* If target hits exit syscall, report to GDB the program is terminated.
|
||||
* In addition, let target run its own exit syscall handler. */
|
||||
program_exited = true;
|
||||
sprintf(fileio_command, "W%02" PRIx32, target->fileio_info->param_1);
|
||||
sprintf(fileio_command, "W%02" PRIx64, target->fileio_info->param_1);
|
||||
} else {
|
||||
LOG_DEBUG("Unknown syscall: %s", target->fileio_info->identifier);
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ TARGET_CORE_SRC = \
|
|||
%D%/target.c \
|
||||
%D%/target_request.c \
|
||||
%D%/testee.c \
|
||||
%D%/semihosting_common.c \
|
||||
%D%/smp.c
|
||||
|
||||
ARMV4_5_SRC = \
|
||||
|
@ -210,6 +211,7 @@ INTEL_IA32_SRC = \
|
|||
%D%/nds32_v3.h \
|
||||
%D%/nds32_v3m.h \
|
||||
%D%/nds32_aice.h \
|
||||
%D%/semihosting_common.h \
|
||||
%D%/stm8.h \
|
||||
%D%/lakemont.h \
|
||||
%D%/x86_32_common.h \
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
* Copyright (C) 2009 by Øyvind Harboe
|
||||
* oyvind.harboe@zylin.com
|
||||
*
|
||||
* Copyright (C) 2018 by Liviu Ionescu
|
||||
* <ilg@livius.net>
|
||||
*
|
||||
* 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
|
||||
|
@ -28,7 +31,6 @@
|
|||
#include <helper/command.h>
|
||||
#include "target.h"
|
||||
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Holds the interface to ARM cores.
|
||||
|
@ -181,32 +183,11 @@ struct arm {
|
|||
/** Flag reporting armv6m based core. */
|
||||
bool is_armv6m;
|
||||
|
||||
/** Flag reporting whether semihosting is active. */
|
||||
bool is_semihosting;
|
||||
|
||||
/** Flag reporting whether semihosting fileio is active. */
|
||||
bool is_semihosting_fileio;
|
||||
|
||||
/** Flag reporting whether semihosting fileio operation is active. */
|
||||
bool semihosting_hit_fileio;
|
||||
|
||||
/** Floating point or VFP version, 0 if disabled. */
|
||||
int arm_vfp_version;
|
||||
|
||||
/** Current semihosting operation. */
|
||||
int semihosting_op;
|
||||
|
||||
/** Current semihosting result. */
|
||||
int semihosting_result;
|
||||
|
||||
/** Value to be returned by semihosting SYS_ERRNO request. */
|
||||
int semihosting_errno;
|
||||
|
||||
int (*setup_semihosting)(struct target *target, int enable);
|
||||
|
||||
/** Semihosting command line. */
|
||||
char *semihosting_cmdline;
|
||||
|
||||
/** Backpointer to the target. */
|
||||
struct target *target;
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
* Copyright (C) 2016 by Square, Inc. *
|
||||
* Steven Stallion <stallion@squareup.com> *
|
||||
* *
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -52,21 +55,6 @@
|
|||
#include <helper/log.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
static int post_result(struct target *target)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
@ -79,7 +67,7 @@ static int post_result(struct target *target)
|
|||
uint32_t spsr;
|
||||
|
||||
/* return value in R0 */
|
||||
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result);
|
||||
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
|
||||
arm->core_cache->reg_list[0].dirty = 1;
|
||||
|
||||
/* LR --> PC */
|
||||
|
@ -105,523 +93,13 @@ static int post_result(struct target *target)
|
|||
* bkpt instruction */
|
||||
|
||||
/* return result in R0 */
|
||||
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result);
|
||||
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
|
||||
arm->core_cache->reg_list[0].dirty = 1;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int do_semihosting(struct target *target)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
struct gdb_fileio_info *fileio_info = target->fileio_info;
|
||||
uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
|
||||
uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
|
||||
uint8_t params[16];
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* TODO: lots of security issues are not considered yet, such as:
|
||||
* - no validation on target provided file descriptors
|
||||
* - no safety checks on opened/deleted/renamed file paths
|
||||
* Beware the target app you use this support with.
|
||||
*
|
||||
* TODO: unsupported semihosting fileio operations could be
|
||||
* implemented if we had a small working area at our disposal.
|
||||
*/
|
||||
switch ((arm->semihosting_op = r0)) {
|
||||
case 0x01: /* SYS_OPEN */
|
||||
retval = target_read_memory(target, r1, 4, 3, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a = target_buffer_get_u32(target, params+0);
|
||||
uint32_t m = target_buffer_get_u32(target, params+4);
|
||||
uint32_t l = target_buffer_get_u32(target, params+8);
|
||||
uint8_t fn[256];
|
||||
retval = target_read_memory(target, a, 1, l, fn);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
fn[l] = 0;
|
||||
if (arm->is_semihosting_fileio) {
|
||||
if (strcmp((char *)fn, ":tt") == 0)
|
||||
arm->semihosting_result = 0;
|
||||
else {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "open";
|
||||
fileio_info->param_1 = a;
|
||||
fileio_info->param_2 = l;
|
||||
fileio_info->param_3 = open_modeflags[m];
|
||||
fileio_info->param_4 = 0644;
|
||||
}
|
||||
} else {
|
||||
if (l <= 255 && m <= 11) {
|
||||
if (strcmp((char *)fn, ":tt") == 0) {
|
||||
if (m < 4)
|
||||
arm->semihosting_result = dup(STDIN_FILENO);
|
||||
else
|
||||
arm->semihosting_result = dup(STDOUT_FILENO);
|
||||
} else {
|
||||
/* cygwin requires the permission setting
|
||||
* otherwise it will fail to reopen a previously
|
||||
* written file */
|
||||
arm->semihosting_result = open((char *)fn, open_modeflags[m], 0644);
|
||||
}
|
||||
arm->semihosting_errno = errno;
|
||||
} else {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02: /* SYS_CLOSE */
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "close";
|
||||
fileio_info->param_1 = fd;
|
||||
} else {
|
||||
arm->semihosting_result = close(fd);
|
||||
arm->semihosting_errno = errno;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03: /* SYS_WRITEC */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "write";
|
||||
fileio_info->param_1 = 1;
|
||||
fileio_info->param_2 = r1;
|
||||
fileio_info->param_3 = 1;
|
||||
} else {
|
||||
unsigned char c;
|
||||
retval = target_read_memory(target, r1, 1, 1, &c);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
putchar(c);
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04: /* SYS_WRITE0 */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
size_t count = 0;
|
||||
for (uint32_t a = r1;; a++) {
|
||||
unsigned char c;
|
||||
retval = target_read_memory(target, a, 1, 1, &c);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
if (c == '\0')
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "write";
|
||||
fileio_info->param_1 = 1;
|
||||
fileio_info->param_2 = r1;
|
||||
fileio_info->param_3 = count;
|
||||
} else {
|
||||
do {
|
||||
unsigned char c;
|
||||
retval = target_read_memory(target, r1++, 1, 1, &c);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
if (!c)
|
||||
break;
|
||||
putchar(c);
|
||||
} while (1);
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x05: /* SYS_WRITE */
|
||||
retval = target_read_memory(target, r1, 4, 3, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
uint32_t a = target_buffer_get_u32(target, params+4);
|
||||
size_t l = target_buffer_get_u32(target, params+8);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "write";
|
||||
fileio_info->param_1 = fd;
|
||||
fileio_info->param_2 = a;
|
||||
fileio_info->param_3 = l;
|
||||
} else {
|
||||
uint8_t *buf = malloc(l);
|
||||
if (!buf) {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = ENOMEM;
|
||||
} else {
|
||||
retval = target_read_buffer(target, a, l, buf);
|
||||
if (retval != ERROR_OK) {
|
||||
free(buf);
|
||||
return retval;
|
||||
}
|
||||
arm->semihosting_result = write(fd, buf, l);
|
||||
arm->semihosting_errno = errno;
|
||||
if (arm->semihosting_result >= 0)
|
||||
arm->semihosting_result = l - arm->semihosting_result;
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x06: /* SYS_READ */
|
||||
retval = target_read_memory(target, r1, 4, 3, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
uint32_t a = target_buffer_get_u32(target, params+4);
|
||||
ssize_t l = target_buffer_get_u32(target, params+8);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "read";
|
||||
fileio_info->param_1 = fd;
|
||||
fileio_info->param_2 = a;
|
||||
fileio_info->param_3 = l;
|
||||
} else {
|
||||
uint8_t *buf = malloc(l);
|
||||
if (!buf) {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = ENOMEM;
|
||||
} else {
|
||||
arm->semihosting_result = read(fd, buf, l);
|
||||
arm->semihosting_errno = errno;
|
||||
if (arm->semihosting_result >= 0) {
|
||||
retval = target_write_buffer(target, a, arm->semihosting_result, buf);
|
||||
if (retval != ERROR_OK) {
|
||||
free(buf);
|
||||
return retval;
|
||||
}
|
||||
arm->semihosting_result = l - arm->semihosting_result;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x07: /* SYS_READC */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
LOG_ERROR("SYS_READC not supported by semihosting fileio");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
arm->semihosting_result = getchar();
|
||||
break;
|
||||
|
||||
case 0x08: /* SYS_ISERROR */
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
arm->semihosting_result = (target_buffer_get_u32(target, params+0) != 0);
|
||||
break;
|
||||
|
||||
case 0x09: /* SYS_ISTTY */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "isatty";
|
||||
fileio_info->param_1 = r1;
|
||||
} else {
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
arm->semihosting_result = isatty(target_buffer_get_u32(target, params+0));
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0a: /* SYS_SEEK */
|
||||
retval = target_read_memory(target, r1, 4, 2, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
off_t pos = target_buffer_get_u32(target, params+4);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "lseek";
|
||||
fileio_info->param_1 = fd;
|
||||
fileio_info->param_2 = pos;
|
||||
fileio_info->param_3 = SEEK_SET;
|
||||
} else {
|
||||
arm->semihosting_result = lseek(fd, pos, SEEK_SET);
|
||||
arm->semihosting_errno = errno;
|
||||
if (arm->semihosting_result == pos)
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0c: /* SYS_FLEN */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
LOG_ERROR("SYS_FLEN not supported by semihosting fileio");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
struct stat buf;
|
||||
arm->semihosting_result = fstat(fd, &buf);
|
||||
if (arm->semihosting_result == -1) {
|
||||
arm->semihosting_errno = errno;
|
||||
arm->semihosting_result = -1;
|
||||
break;
|
||||
}
|
||||
arm->semihosting_result = buf.st_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0e: /* SYS_REMOVE */
|
||||
retval = target_read_memory(target, r1, 4, 2, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a = target_buffer_get_u32(target, params+0);
|
||||
uint32_t l = target_buffer_get_u32(target, params+4);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "unlink";
|
||||
fileio_info->param_1 = a;
|
||||
fileio_info->param_2 = l;
|
||||
} else {
|
||||
if (l <= 255) {
|
||||
uint8_t fn[256];
|
||||
retval = target_read_memory(target, a, 1, l, fn);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
fn[l] = 0;
|
||||
arm->semihosting_result = remove((char *)fn);
|
||||
arm->semihosting_errno = errno;
|
||||
} else {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0f: /* SYS_RENAME */
|
||||
retval = target_read_memory(target, r1, 4, 4, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a1 = target_buffer_get_u32(target, params+0);
|
||||
uint32_t l1 = target_buffer_get_u32(target, params+4);
|
||||
uint32_t a2 = target_buffer_get_u32(target, params+8);
|
||||
uint32_t l2 = target_buffer_get_u32(target, params+12);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "rename";
|
||||
fileio_info->param_1 = a1;
|
||||
fileio_info->param_2 = l1;
|
||||
fileio_info->param_3 = a2;
|
||||
fileio_info->param_4 = l2;
|
||||
} else {
|
||||
if (l1 <= 255 && l2 <= 255) {
|
||||
uint8_t fn1[256], fn2[256];
|
||||
retval = target_read_memory(target, a1, 1, l1, fn1);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
retval = target_read_memory(target, a2, 1, l2, fn2);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
fn1[l1] = 0;
|
||||
fn2[l2] = 0;
|
||||
arm->semihosting_result = rename((char *)fn1, (char *)fn2);
|
||||
arm->semihosting_errno = errno;
|
||||
} else {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x11: /* SYS_TIME */
|
||||
arm->semihosting_result = time(NULL);
|
||||
break;
|
||||
|
||||
case 0x13: /* SYS_ERRNO */
|
||||
arm->semihosting_result = arm->semihosting_errno;
|
||||
break;
|
||||
|
||||
case 0x15: /* SYS_GET_CMDLINE */
|
||||
retval = target_read_memory(target, r1, 4, 2, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a = target_buffer_get_u32(target, params+0);
|
||||
uint32_t l = target_buffer_get_u32(target, params+4);
|
||||
char *arg = arm->semihosting_cmdline != NULL ? arm->semihosting_cmdline : "";
|
||||
uint32_t s = strlen(arg) + 1;
|
||||
if (l < s)
|
||||
arm->semihosting_result = -1;
|
||||
else {
|
||||
retval = target_write_buffer(target, a, s, (uint8_t *)arg);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x16: /* SYS_HEAPINFO */
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a = target_buffer_get_u32(target, params+0);
|
||||
/* tell the remote we have no idea */
|
||||
memset(params, 0, 4*4);
|
||||
retval = target_write_memory(target, a, 4, 4, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x18: /* angel_SWIreason_ReportException */
|
||||
switch (r1) {
|
||||
case 0x20026: /* ADP_Stopped_ApplicationExit */
|
||||
fprintf(stderr, "semihosting: *** application exited ***\n");
|
||||
break;
|
||||
case 0x20000: /* ADP_Stopped_BranchThroughZero */
|
||||
case 0x20001: /* ADP_Stopped_UndefinedInstr */
|
||||
case 0x20002: /* ADP_Stopped_SoftwareInterrupt */
|
||||
case 0x20003: /* ADP_Stopped_PrefetchAbort */
|
||||
case 0x20004: /* ADP_Stopped_DataAbort */
|
||||
case 0x20005: /* ADP_Stopped_AddressException */
|
||||
case 0x20006: /* ADP_Stopped_IRQ */
|
||||
case 0x20007: /* ADP_Stopped_FIQ */
|
||||
case 0x20020: /* ADP_Stopped_BreakPoint */
|
||||
case 0x20021: /* ADP_Stopped_WatchPoint */
|
||||
case 0x20022: /* ADP_Stopped_StepComplete */
|
||||
case 0x20023: /* ADP_Stopped_RunTimeErrorUnknown */
|
||||
case 0x20024: /* ADP_Stopped_InternalError */
|
||||
case 0x20025: /* ADP_Stopped_UserInterruption */
|
||||
case 0x20027: /* ADP_Stopped_StackOverflow */
|
||||
case 0x20028: /* ADP_Stopped_DivisionByZero */
|
||||
case 0x20029: /* ADP_Stopped_OSSpecific */
|
||||
default:
|
||||
fprintf(stderr, "semihosting: exception %#x\n",
|
||||
(unsigned) r1);
|
||||
}
|
||||
return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||
|
||||
case 0x12: /* SYS_SYSTEM */
|
||||
/* Provide SYS_SYSTEM functionality. Uses the
|
||||
* libc system command, there may be a reason *NOT*
|
||||
* to use this, but as I can't think of one, I
|
||||
* implemented it this way.
|
||||
*/
|
||||
retval = target_read_memory(target, r1, 4, 2, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t len = target_buffer_get_u32(target, params+4);
|
||||
uint32_t c_ptr = target_buffer_get_u32(target, params);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "system";
|
||||
fileio_info->param_1 = c_ptr;
|
||||
fileio_info->param_2 = len;
|
||||
} else {
|
||||
uint8_t cmd[256];
|
||||
if (len > 255) {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = EINVAL;
|
||||
} else {
|
||||
memset(cmd, 0x0, 256);
|
||||
retval = target_read_memory(target, c_ptr, 1, len, cmd);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else
|
||||
arm->semihosting_result = system((const char *)cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x0d: /* SYS_TMPNAM */
|
||||
case 0x10: /* SYS_CLOCK */
|
||||
case 0x17: /* angel_SWIreason_EnterSVC */
|
||||
case 0x30: /* SYS_ELAPSED */
|
||||
case 0x31: /* SYS_TICKFREQ */
|
||||
default:
|
||||
fprintf(stderr, "semihosting: unsupported call %#x\n",
|
||||
(unsigned) r0);
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = ENOTSUP;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
/* To avoid uneccessary duplication, semihosting prepares the
|
||||
* fileio_info structure out-of-band when the target halts. See
|
||||
* do_semihosting for more detail.
|
||||
*/
|
||||
if (!arm->is_semihosting_fileio || !arm->semihosting_hit_fileio)
|
||||
return ERROR_FAIL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int gdb_fileio_end(struct target *target, int result, int fileio_errno, bool ctrl_c)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
struct gdb_fileio_info *fileio_info = target->fileio_info;
|
||||
|
||||
/* clear pending status */
|
||||
arm->semihosting_hit_fileio = false;
|
||||
|
||||
arm->semihosting_result = result;
|
||||
arm->semihosting_errno = fileio_errno;
|
||||
|
||||
/* Some fileio results do not match up with what the semihosting
|
||||
* operation expects; for these operations, we munge the results
|
||||
* below:
|
||||
*/
|
||||
switch (arm->semihosting_op) {
|
||||
case 0x05: /* SYS_WRITE */
|
||||
if (result < 0)
|
||||
arm->semihosting_result = fileio_info->param_3;
|
||||
else
|
||||
arm->semihosting_result = 0;
|
||||
break;
|
||||
|
||||
case 0x06: /* SYS_READ */
|
||||
if (result == (int)fileio_info->param_3)
|
||||
arm->semihosting_result = 0;
|
||||
if (result <= 0)
|
||||
arm->semihosting_result = fileio_info->param_3;
|
||||
break;
|
||||
|
||||
case 0x0a: /* SYS_SEEK */
|
||||
if (result > 0)
|
||||
arm->semihosting_result = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return post_result(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize ARM semihosting support.
|
||||
*
|
||||
|
@ -630,14 +108,9 @@ static int gdb_fileio_end(struct target *target, int result, int fileio_errno, b
|
|||
*/
|
||||
int arm_semihosting_init(struct target *target)
|
||||
{
|
||||
target->fileio_info = malloc(sizeof(*target->fileio_info));
|
||||
if (target->fileio_info == NULL) {
|
||||
LOG_ERROR("out of memory");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
target->type->get_gdb_fileio_info = get_gdb_fileio_info;
|
||||
target->type->gdb_fileio_end = gdb_fileio_end;
|
||||
struct arm *arm = target_to_arm(target);
|
||||
assert(arm->setup_semihosting);
|
||||
semihosting_common_init(target, arm->setup_semihosting, post_result);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
@ -662,7 +135,11 @@ int arm_semihosting(struct target *target, int *retval)
|
|||
uint32_t pc, lr, spsr;
|
||||
struct reg *r;
|
||||
|
||||
if (!arm->is_semihosting)
|
||||
struct semihosting *semihosting = target->semihosting;
|
||||
if (!semihosting)
|
||||
return 0;
|
||||
|
||||
if (!semihosting->is_active)
|
||||
return 0;
|
||||
|
||||
if (is_arm7_9(target_to_arm7_9(target)) ||
|
||||
|
@ -766,10 +243,24 @@ int arm_semihosting(struct target *target, int *retval)
|
|||
/* Perform semihosting if we are not waiting on a fileio
|
||||
* operation to complete.
|
||||
*/
|
||||
if (!arm->semihosting_hit_fileio) {
|
||||
*retval = do_semihosting(target);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed semihosting operation");
|
||||
if (!semihosting->hit_fileio) {
|
||||
/* TODO: update for 64-bits */
|
||||
uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
|
||||
uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
|
||||
|
||||
semihosting->op = r0;
|
||||
semihosting->param = r1;
|
||||
semihosting->word_size_bytes = 4;
|
||||
|
||||
/* Check for ARM operation numbers. */
|
||||
if (0 <= semihosting->op && semihosting->op <= 0x31) {
|
||||
*retval = semihosting_common(target);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed semihosting operation");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* Unknown operation number, not a semihosting call. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -777,13 +268,8 @@ int arm_semihosting(struct target *target, int *retval)
|
|||
/* Post result to target if we are not waiting on a fileio
|
||||
* operation to complete:
|
||||
*/
|
||||
if (!arm->semihosting_hit_fileio) {
|
||||
*retval = post_result(target);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to post semihosting result");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (semihosting->is_resumable && !semihosting->hit_fileio) {
|
||||
/* Resume right after the BRK instruction. */
|
||||
*retval = target_resume(target, 1, 0, 0, 0);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to resume target");
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef OPENOCD_TARGET_ARM_SEMIHOSTING_H
|
||||
#define OPENOCD_TARGET_ARM_SEMIHOSTING_H
|
||||
|
||||
#include "semihosting_common.h"
|
||||
|
||||
int arm_semihosting_init(struct target *target);
|
||||
int arm_semihosting(struct target *target, int *retval);
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
* Copyright (C) 2008 by Oyvind Harboe *
|
||||
* oyvind.harboe@zylin.com *
|
||||
* *
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -34,6 +37,7 @@
|
|||
#include <helper/binarybuffer.h>
|
||||
#include "algorithm.h"
|
||||
#include "register.h"
|
||||
#include "semihosting_common.h"
|
||||
|
||||
/* offsets into armv4_5 core register cache */
|
||||
enum {
|
||||
|
@ -748,7 +752,7 @@ int arm_arch_state(struct target *target)
|
|||
}
|
||||
|
||||
/* avoid filling log waiting for fileio reply */
|
||||
if (arm->semihosting_hit_fileio)
|
||||
if (target->semihosting->hit_fileio)
|
||||
return ERROR_OK;
|
||||
|
||||
LOG_USER("target halted in %s state due to %s, current mode: %s\n"
|
||||
|
@ -758,8 +762,8 @@ int arm_arch_state(struct target *target)
|
|||
arm_mode_name(arm->core_mode),
|
||||
buf_get_u32(arm->cpsr->value, 0, 32),
|
||||
buf_get_u32(arm->pc->value, 0, 32),
|
||||
arm->is_semihosting ? ", semihosting" : "",
|
||||
arm->is_semihosting_fileio ? " fileio" : "");
|
||||
target->semihosting->is_active ? ", semihosting" : "",
|
||||
target->semihosting->is_fileio ? " fileio" : "");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
@ -1094,119 +1098,10 @@ static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
|
|||
return JIM_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_arm_semihosting_command)
|
||||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
|
||||
if (target == NULL) {
|
||||
LOG_ERROR("No target selected");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
if (!is_arm(arm)) {
|
||||
command_print(CMD_CTX, "current target isn't an ARM");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!arm->setup_semihosting) {
|
||||
command_print(CMD_CTX, "semihosting not supported for current target");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (CMD_ARGC > 0) {
|
||||
int semihosting;
|
||||
|
||||
COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting);
|
||||
|
||||
if (!target_was_examined(target)) {
|
||||
LOG_ERROR("Target not examined yet");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (arm->setup_semihosting(target, semihosting) != ERROR_OK) {
|
||||
LOG_ERROR("Failed to Configure semihosting");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* FIXME never let that "catch" be dropped! */
|
||||
arm->is_semihosting = semihosting;
|
||||
}
|
||||
|
||||
command_print(CMD_CTX, "semihosting is %s",
|
||||
arm->is_semihosting
|
||||
? "enabled" : "disabled");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_arm_semihosting_fileio_command)
|
||||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
|
||||
if (target == NULL) {
|
||||
LOG_ERROR("No target selected");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
if (!is_arm(arm)) {
|
||||
command_print(CMD_CTX, "current target isn't an ARM");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!arm->is_semihosting) {
|
||||
command_print(CMD_CTX, "semihosting is not enabled");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (CMD_ARGC > 0)
|
||||
COMMAND_PARSE_ENABLE(CMD_ARGV[0], arm->is_semihosting_fileio);
|
||||
|
||||
command_print(CMD_CTX, "semihosting fileio is %s",
|
||||
arm->is_semihosting_fileio
|
||||
? "enabled" : "disabled");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_arm_semihosting_cmdline)
|
||||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
unsigned int i;
|
||||
|
||||
if (target == NULL) {
|
||||
LOG_ERROR("No target selected");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
if (!is_arm(arm)) {
|
||||
command_print(CMD_CTX, "current target isn't an ARM");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!arm->setup_semihosting) {
|
||||
command_print(CMD_CTX, "semihosting not supported for current target");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
free(arm->semihosting_cmdline);
|
||||
arm->semihosting_cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL;
|
||||
|
||||
for (i = 1; i < CMD_ARGC; i++) {
|
||||
char *cmdline = alloc_printf("%s %s", arm->semihosting_cmdline, CMD_ARGV[i]);
|
||||
if (cmdline == NULL)
|
||||
break;
|
||||
free(arm->semihosting_cmdline);
|
||||
arm->semihosting_cmdline = cmdline;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_command);
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command);
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command);
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_cmdline);
|
||||
|
||||
static const struct command_registration arm_exec_command_handlers[] = {
|
||||
{
|
||||
|
@ -1245,26 +1140,32 @@ static const struct command_registration arm_exec_command_handlers[] = {
|
|||
},
|
||||
{
|
||||
"semihosting",
|
||||
.handler = handle_arm_semihosting_command,
|
||||
.handler = handle_common_semihosting_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "['enable'|'disable']",
|
||||
.help = "activate support for semihosting operations",
|
||||
},
|
||||
{
|
||||
"semihosting_cmdline",
|
||||
.handler = handle_arm_semihosting_cmdline,
|
||||
.handler = handle_common_semihosting_cmdline,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "arguments",
|
||||
.help = "command line arguments to be passed to program",
|
||||
},
|
||||
{
|
||||
"semihosting_fileio",
|
||||
.handler = handle_arm_semihosting_fileio_command,
|
||||
.handler = handle_common_semihosting_fileio_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "['enable'|'disable']",
|
||||
.help = "activate support for semihosting fileio operations",
|
||||
},
|
||||
|
||||
{
|
||||
"semihosting_resexit",
|
||||
.handler = handle_common_semihosting_resumable_exit_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "['enable'|'disable']",
|
||||
.help = "activate support for semihosting resumable exit",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
const struct command_registration arm_command_handlers[] = {
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
* Copyright (C) 2007,2008 Øyvind Harboe *
|
||||
* oyvind.harboe@zylin.com *
|
||||
* *
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -37,6 +40,7 @@
|
|||
#include "armv7m.h"
|
||||
#include "algorithm.h"
|
||||
#include "register.h"
|
||||
#include "semihosting_common.h"
|
||||
|
||||
#if 0
|
||||
#define _DEBUG_INSTRUCTION_EXECUTION_
|
||||
|
@ -537,7 +541,7 @@ int armv7m_arch_state(struct target *target)
|
|||
uint32_t ctrl, sp;
|
||||
|
||||
/* avoid filling log waiting for fileio reply */
|
||||
if (arm->semihosting_hit_fileio)
|
||||
if (target->semihosting->hit_fileio)
|
||||
return ERROR_OK;
|
||||
|
||||
ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32);
|
||||
|
@ -552,8 +556,8 @@ int armv7m_arch_state(struct target *target)
|
|||
buf_get_u32(arm->pc->value, 0, 32),
|
||||
(ctrl & 0x02) ? 'p' : 'm',
|
||||
sp,
|
||||
arm->is_semihosting ? ", semihosting" : "",
|
||||
arm->is_semihosting_fileio ? " fileio" : "");
|
||||
target->semihosting->is_active ? ", semihosting" : "",
|
||||
target->semihosting->is_fileio ? " fileio" : "");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2015 by David Ung *
|
||||
* *
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -36,6 +39,7 @@
|
|||
#include "armv8_opcodes.h"
|
||||
#include "target.h"
|
||||
#include "target_type.h"
|
||||
#include "semihosting_common.h"
|
||||
|
||||
static const char * const armv8_state_strings[] = {
|
||||
"AArch32", "Thumb", "Jazelle", "ThumbEE", "AArch64",
|
||||
|
@ -1046,7 +1050,7 @@ int armv8_aarch64_state(struct target *target)
|
|||
armv8_mode_name(arm->core_mode),
|
||||
buf_get_u32(arm->cpsr->value, 0, 32),
|
||||
buf_get_u64(arm->pc->value, 0, 64),
|
||||
arm->is_semihosting ? ", semihosting" : "");
|
||||
target->semihosting->is_active ? ", semihosting" : "");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
|
|
@ -2339,63 +2339,66 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil
|
|||
fileio_info->identifier = NULL;
|
||||
}
|
||||
|
||||
uint32_t reg_r0, reg_r1, reg_r2;
|
||||
nds32_get_mapped_reg(nds32, R0, ®_r0);
|
||||
nds32_get_mapped_reg(nds32, R1, ®_r1);
|
||||
nds32_get_mapped_reg(nds32, R2, ®_r2);
|
||||
|
||||
switch (syscall_id) {
|
||||
case NDS32_SYSCALL_EXIT:
|
||||
fileio_info->identifier = malloc(5);
|
||||
sprintf(fileio_info->identifier, "exit");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
break;
|
||||
case NDS32_SYSCALL_OPEN:
|
||||
{
|
||||
uint8_t filename[256];
|
||||
fileio_info->identifier = malloc(5);
|
||||
sprintf(fileio_info->identifier, "open");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of path */
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
|
||||
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_4));
|
||||
fileio_info->param_3 = reg_r1;
|
||||
fileio_info->param_4 = reg_r2;
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r0, 256, filename);
|
||||
fileio_info->param_2 = strlen((char *)filename) + 1;
|
||||
}
|
||||
break;
|
||||
case NDS32_SYSCALL_CLOSE:
|
||||
fileio_info->identifier = malloc(6);
|
||||
sprintf(fileio_info->identifier, "close");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
break;
|
||||
case NDS32_SYSCALL_READ:
|
||||
fileio_info->identifier = malloc(5);
|
||||
sprintf(fileio_info->identifier, "read");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
fileio_info->param_3 = reg_r2;
|
||||
break;
|
||||
case NDS32_SYSCALL_WRITE:
|
||||
fileio_info->identifier = malloc(6);
|
||||
sprintf(fileio_info->identifier, "write");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
fileio_info->param_3 = reg_r2;
|
||||
break;
|
||||
case NDS32_SYSCALL_LSEEK:
|
||||
fileio_info->identifier = malloc(6);
|
||||
sprintf(fileio_info->identifier, "lseek");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
fileio_info->param_3 = reg_r2;
|
||||
break;
|
||||
case NDS32_SYSCALL_UNLINK:
|
||||
{
|
||||
uint8_t filename[256];
|
||||
fileio_info->identifier = malloc(7);
|
||||
sprintf(fileio_info->identifier, "unlink");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of path */
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r0, 256, filename);
|
||||
fileio_info->param_2 = strlen((char *)filename) + 1;
|
||||
}
|
||||
break;
|
||||
|
@ -2404,61 +2407,57 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil
|
|||
uint8_t filename[256];
|
||||
fileio_info->identifier = malloc(7);
|
||||
sprintf(fileio_info->identifier, "rename");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of old path */
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
|
||||
fileio_info->param_3 = reg_r1;
|
||||
/* reserve fileio_info->param_4 for length of new path */
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r0, 256, filename);
|
||||
fileio_info->param_2 = strlen((char *)filename) + 1;
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_3,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r1, 256, filename);
|
||||
fileio_info->param_4 = strlen((char *)filename) + 1;
|
||||
}
|
||||
break;
|
||||
case NDS32_SYSCALL_FSTAT:
|
||||
fileio_info->identifier = malloc(6);
|
||||
sprintf(fileio_info->identifier, "fstat");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
break;
|
||||
case NDS32_SYSCALL_STAT:
|
||||
{
|
||||
uint8_t filename[256];
|
||||
fileio_info->identifier = malloc(5);
|
||||
sprintf(fileio_info->identifier, "stat");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of old path */
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
|
||||
fileio_info->param_3 = reg_r1;
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r0, 256, filename);
|
||||
fileio_info->param_2 = strlen((char *)filename) + 1;
|
||||
}
|
||||
break;
|
||||
case NDS32_SYSCALL_GETTIMEOFDAY:
|
||||
fileio_info->identifier = malloc(13);
|
||||
sprintf(fileio_info->identifier, "gettimeofday");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
break;
|
||||
case NDS32_SYSCALL_ISATTY:
|
||||
fileio_info->identifier = malloc(7);
|
||||
sprintf(fileio_info->identifier, "isatty");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
break;
|
||||
case NDS32_SYSCALL_SYSTEM:
|
||||
{
|
||||
uint8_t command[256];
|
||||
fileio_info->identifier = malloc(7);
|
||||
sprintf(fileio_info->identifier, "system");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of old path */
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, command);
|
||||
target->type->read_buffer(target, reg_r0, 256, command);
|
||||
fileio_info->param_2 = strlen((char *)command) + 1;
|
||||
}
|
||||
break;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,163 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* Copyright (C) 2009 by Marvell Technology Group Ltd. *
|
||||
* Written by Nicolas Pitre <nico@marvell.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/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_TARGET_SEMIHOSTING_COMMON_H
|
||||
#define OPENOCD_TARGET_SEMIHOSTING_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* According to:
|
||||
* "Semihosting for AArch32 and AArch64, Release 2.0"
|
||||
* https://static.docs.arm.com/100863/0200/semihosting.pdf
|
||||
* from ARM Ltd.
|
||||
*
|
||||
* The available semihosting operation numbers passed in R0 are allocated
|
||||
* as follows:
|
||||
* - 0x00-0x31 Used by ARM.
|
||||
* - 0x32-0xFF Reserved for future use by ARM.
|
||||
* - 0x100-0x1FF Reserved for user applications. These are not used by ARM.
|
||||
* However, if you are writing your own SVC operations, you are advised
|
||||
* to use a different SVC number rather than using the semihosted
|
||||
* SVC number and these operation type numbers.
|
||||
* - 0x200-0xFFFFFFFF Undefined and currently unused. It is recommended
|
||||
* that you do not use these.
|
||||
*/
|
||||
|
||||
enum semihosting_operation_numbers {
|
||||
/*
|
||||
* ARM semihosting operations, in lexicographic order.
|
||||
*/
|
||||
SEMIHOSTING_ENTER_SVC = 0x17, /* DEPRECATED */
|
||||
|
||||
SEMIHOSTING_SYS_CLOSE = 0x02,
|
||||
SEMIHOSTING_SYS_CLOCK = 0x10,
|
||||
SEMIHOSTING_SYS_ELAPSED = 0x30,
|
||||
SEMIHOSTING_SYS_ERRNO = 0x13,
|
||||
SEMIHOSTING_SYS_EXIT = 0x18,
|
||||
SEMIHOSTING_SYS_EXIT_EXTENDED = 0x20,
|
||||
SEMIHOSTING_SYS_FLEN = 0x0C,
|
||||
SEMIHOSTING_SYS_GET_CMDLINE = 0x15,
|
||||
SEMIHOSTING_SYS_HEAPINFO = 0x16,
|
||||
SEMIHOSTING_SYS_ISERROR = 0x08,
|
||||
SEMIHOSTING_SYS_ISTTY = 0x09,
|
||||
SEMIHOSTING_SYS_OPEN = 0x01,
|
||||
SEMIHOSTING_SYS_READ = 0x06,
|
||||
SEMIHOSTING_SYS_READC = 0x07,
|
||||
SEMIHOSTING_SYS_REMOVE = 0x0E,
|
||||
SEMIHOSTING_SYS_RENAME = 0x0F,
|
||||
SEMIHOSTING_SYS_SEEK = 0x0A,
|
||||
SEMIHOSTING_SYS_SYSTEM = 0x12,
|
||||
SEMIHOSTING_SYS_TICKFREQ = 0x31,
|
||||
SEMIHOSTING_SYS_TIME = 0x11,
|
||||
SEMIHOSTING_SYS_TMPNAM = 0x0D,
|
||||
SEMIHOSTING_SYS_WRITE = 0x05,
|
||||
SEMIHOSTING_SYS_WRITEC = 0x03,
|
||||
SEMIHOSTING_SYS_WRITE0 = 0x04,
|
||||
};
|
||||
|
||||
/*
|
||||
* Codes used by SEMIHOSTING_SYS_EXIT (formerly
|
||||
* SEMIHOSTING_REPORT_EXCEPTION).
|
||||
* On 64-bits, the exit code is passed explicitly.
|
||||
*/
|
||||
enum semihosting_reported_exceptions {
|
||||
/* On 32 bits, use it for exit(0) */
|
||||
ADP_STOPPED_APPLICATION_EXIT = ((2 << 16) + 38),
|
||||
/* On 32 bits, use it for exit(1) */
|
||||
ADP_STOPPED_RUN_TIME_ERROR = ((2 << 16) + 35),
|
||||
};
|
||||
|
||||
struct target;
|
||||
|
||||
/*
|
||||
* A pointer to this structure was added to the target structure.
|
||||
*/
|
||||
struct semihosting {
|
||||
|
||||
/** A flag reporting whether semihosting is active. */
|
||||
bool is_active;
|
||||
|
||||
/** A flag reporting whether semihosting fileio is active. */
|
||||
bool is_fileio;
|
||||
|
||||
/** A flag reporting whether semihosting fileio operation is active. */
|
||||
bool hit_fileio;
|
||||
|
||||
/** Most are resumable, except the two exit calls. */
|
||||
bool is_resumable;
|
||||
|
||||
/**
|
||||
* When SEMIHOSTING_SYS_EXIT is called outside a debug session,
|
||||
* things are simple, the openocd process calls exit() and passes
|
||||
* the value returned by the target.
|
||||
* When SEMIHOSTING_SYS_EXIT is called during a debug session,
|
||||
* by default execution returns to the debugger, leaving the
|
||||
* debugger in a HALT state, similar to the state entered when
|
||||
* encountering a break.
|
||||
* In some use cases, it is useful to have SEMIHOSTING_SYS_EXIT
|
||||
* return normally, as any semihosting call, and do not break
|
||||
* to the debugger.
|
||||
* The standard allows this to happen, but the condition
|
||||
* to trigger it is a bit obscure ("by performing an RDI_Execute
|
||||
* request or equivalent").
|
||||
*
|
||||
* To make the SEMIHOSTING_SYS_EXIT call return normally, enable
|
||||
* this variable via the dedicated command (default: disabled).
|
||||
*/
|
||||
bool has_resumable_exit;
|
||||
|
||||
/** The Target (hart) word size; 8 for 64-bits targets. */
|
||||
size_t word_size_bytes;
|
||||
|
||||
/** The current semihosting operation (R0 on ARM). */
|
||||
int op;
|
||||
|
||||
/** The current semihosting parameter (R1 or ARM). */
|
||||
uint64_t param;
|
||||
|
||||
/**
|
||||
* The current semihosting result to be returned to the application.
|
||||
* Usually 0 for success, -1 for error,
|
||||
* but sometimes a useful value, even a pointer.
|
||||
*/
|
||||
int64_t result;
|
||||
|
||||
/** The value to be returned by semihosting SYS_ERRNO request. */
|
||||
int sys_errno;
|
||||
|
||||
/** The semihosting command line to be passed to the target. */
|
||||
char *cmdline;
|
||||
|
||||
/** The current time when 'execution starts' */
|
||||
clock_t setup_time;
|
||||
|
||||
int (*setup)(struct target *target, int enable);
|
||||
int (*post_result)(struct target *target);
|
||||
};
|
||||
|
||||
int semihosting_common_init(struct target *target, void *setup,
|
||||
void *post_result);
|
||||
int semihosting_common(struct target *target);
|
||||
|
||||
#endif /* OPENOCD_TARGET_SEMIHOSTING_COMMON_H */
|
|
@ -205,6 +205,9 @@ struct target {
|
|||
|
||||
/* file-I/O information for host to do syscall */
|
||||
struct gdb_fileio_info *fileio_info;
|
||||
|
||||
/* The semihosting information, extracted from the target. */
|
||||
struct semihosting *semihosting;
|
||||
};
|
||||
|
||||
struct target_list {
|
||||
|
@ -214,10 +217,10 @@ struct target_list {
|
|||
|
||||
struct gdb_fileio_info {
|
||||
char *identifier;
|
||||
uint32_t param_1;
|
||||
uint32_t param_2;
|
||||
uint32_t param_3;
|
||||
uint32_t param_4;
|
||||
uint64_t param_1;
|
||||
uint64_t param_2;
|
||||
uint64_t param_3;
|
||||
uint64_t param_4;
|
||||
};
|
||||
|
||||
/** Returns the instance-specific name of the specified target. */
|
||||
|
|
Loading…
Reference in New Issue