basic ARM semihosting support

Semihosting enables code running on an ARM target to use the
I/O facilities on the host computer. The target application must
be linked against a library that forwards operation requests by
using the SVC instruction that is trapped at the Supervisor Call
vector by the debugger.  The "hosted" library version provided
with CodeSourcery's Sourcery G++ Lite for ARM EABI is one example.

This is currently available for ARM9 processors, but any ARM
variant should be able to support this with little additional work.

Tested using binaries compiled with Sourcery G++ Lite 2009q1-161
and ARM RVCT 3.0.

[dbrownell@users.sourceforge.net: doc tweaks, NEWS]

Signed-off-by: Nicolas Pitre <nico@marvell.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
__archive__
Nicolas Pitre 2009-12-03 17:27:13 -05:00 committed by David Brownell
parent f62c035c52
commit ed59dfc80a
6 changed files with 528 additions and 0 deletions

1
NEWS
View File

@ -17,6 +17,7 @@ Target Layer:
- register names use "sp" not "r13" - register names use "sp" not "r13"
- add top-level "mcr" and "mrc" commands, replacing - add top-level "mcr" and "mrc" commands, replacing
various core-specific operations various core-specific operations
- basic semihosting support
ARM11 ARM11
- Preliminary ETM and ETB hookup - Preliminary ETM and ETB hookup
- accelerated "flash erase_check" - accelerated "flash erase_check"

View File

@ -5674,6 +5674,17 @@ cables (FT2232), but might be unsafe if used with targets running at very low
speeds, like the 32kHz startup clock of an AT91RM9200. speeds, like the 32kHz startup clock of an AT91RM9200.
@end deffn @end deffn
@deffn Command {arm7_9 semihosting} [@option{enable}|@option{disable}]
Display status of semihosting, after optionally changing that status.
Semihosting allows for code executing on an ARM target to use the
I/O facilities on the host computer i.e. the system where OpenOCD
is running. The target application must be linked against a library
implementing the ARM semihosting convention that forwards operation
requests by using a special SVC instruction that is trapped at the
Supervisor Call vector by OpenOCD.
@end deffn
@subsection ARM720T specific commands @subsection ARM720T specific commands
@cindex ARM720T @cindex ARM720T

View File

@ -78,6 +78,7 @@ ARM_DEBUG_SRC = \
arm_jtag.c \ arm_jtag.c \
arm_disassembler.c \ arm_disassembler.c \
arm_simulator.c \ arm_simulator.c \
arm_semihosting.c \
arm_adi_v5.c \ arm_adi_v5.c \
embeddedice.c \ embeddedice.c \
trace.c \ trace.c \
@ -101,6 +102,7 @@ noinst_HEADERS = \
arm_adi_v5.h \ arm_adi_v5.h \
arm_disassembler.h \ arm_disassembler.h \
arm_simulator.h \ arm_simulator.h \
arm_semihosting.h \
arm7_9_common.h \ arm7_9_common.h \
arm7tdmi.h \ arm7tdmi.h \
arm720t.h \ arm720t.h \

View File

@ -36,6 +36,7 @@
#include "etm.h" #include "etm.h"
#include <helper/time_support.h> #include <helper/time_support.h>
#include "arm_simulator.h" #include "arm_simulator.h"
#include "arm_semihosting.h"
#include "algorithm.h" #include "algorithm.h"
#include "register.h" #include "register.h"
@ -915,6 +916,9 @@ int arm7_9_poll(struct target *target)
} }
} }
if (arm_semihosting(target, &retval) != 0)
return retval;
if ((retval = target_call_event_callbacks(target, TARGET_EVENT_HALTED)) != ERROR_OK) if ((retval = target_call_event_callbacks(target, TARGET_EVENT_HALTED)) != ERROR_OK)
{ {
return retval; return retval;
@ -2814,6 +2818,39 @@ COMMAND_HANDLER(handle_arm7_9_dcc_downloads_command)
return ERROR_OK; return ERROR_OK;
} }
COMMAND_HANDLER(handle_arm7_9_semihosting_command)
{
struct target *target = get_current_target(CMD_CTX);
struct arm7_9_common *arm7_9 = target_to_arm7_9(target);
if (!is_arm7_9(arm7_9))
{
command_print(CMD_CTX, "current target isn't an ARM7/ARM9 target");
return ERROR_TARGET_INVALID;
}
if (CMD_ARGC > 0)
{
COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting_active);
/* TODO: support other methods if vector catch is unavailable */
if (arm7_9->has_vector_catch) {
struct reg *vector_catch = &arm7_9->eice_cache->reg_list[EICE_VEC_CATCH];
if (!vector_catch->valid)
embeddedice_read_reg(vector_catch);
buf_set_u32(vector_catch->value, 2, 1, semihosting_active);
embeddedice_store_reg(vector_catch);
} else if (semihosting_active) {
command_print(CMD_CTX, "vector catch unavailable");
semihosting_active = 0;
}
}
command_print(CMD_CTX, "semihosting is %s", (semihosting_active) ? "enabled" : "disabled");
return ERROR_OK;
}
int arm7_9_init_arch_info(struct target *target, struct arm7_9_common *arm7_9) int arm7_9_init_arch_info(struct target *target, struct arm7_9_common *arm7_9)
{ {
int retval = ERROR_OK; int retval = ERROR_OK;
@ -2867,6 +2904,13 @@ static const struct command_registration arm7_9_any_command_handlers[] = {
.usage = "<enable | disable>", .usage = "<enable | disable>",
.help = "use DCC downloads for larger memory writes", .help = "use DCC downloads for larger memory writes",
}, },
{
"semihosting",
.handler = &handle_arm7_9_semihosting_command,
.mode = COMMAND_EXEC,
.usage = "<enable | disable>",
.help = "activate support for semihosting operations",
},
COMMAND_REGISTRATION_DONE COMMAND_REGISTRATION_DONE
}; };
const struct command_registration arm7_9_command_handlers[] = { const struct command_registration arm7_9_command_handlers[] = {

View File

@ -0,0 +1,442 @@
/***************************************************************************
* 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/**
* @file
* Hold ARM semihosting support.
*
* Semihosting enables code running on an ARM target to use the I/O
* facilities on the host computer. The target application must be linked
* against a library that forwards operation requests by using the SVC
* instruction trapped at the Supervisor Call vector by the debugger.
* Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
* from ARM Ltd.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "armv4_5.h"
#include "register.h"
#include "arm_semihosting.h"
#include <helper/binarybuffer.h>
#include <helper/log.h>
/* TODO: this needs to be per target */
int semihosting_active;
int semihosting_errno;
static int do_semihosting(struct target *target)
{
struct arm *armv4_5 = target_to_armv4_5(target);
uint32_t r0 = buf_get_u32(armv4_5->core_cache->reg_list[0].value, 0, 32);
uint32_t r1 = buf_get_u32(armv4_5->core_cache->reg_list[1].value, 0, 32);
uint32_t lr = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, ARMV4_5_MODE_SVC, 14).value, 0, 32);
uint32_t spsr = buf_get_u32(armv4_5->spsr->value, 0, 32);;
uint8_t params[16];
int retval, result;
/*
* 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.
*/
switch (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);
if (l <= 255 && m <= 11) {
uint8_t fn[256];
int mode;
retval = target_read_memory(target, a, 1, l, fn);
if (retval != ERROR_OK)
return retval;
fn[l] = 0;
if (m & 0x2)
mode = O_RDWR;
else if (m & 0xc)
mode = O_WRONLY;
else
mode = O_RDONLY;
if (m >= 8)
mode |= O_CREAT|O_APPEND;
else if (m >= 4)
mode |= O_CREAT|O_TRUNC;
if (strcmp((char *)fn, ":tt") == 0) {
if ((mode & 3) == 0)
result = dup(0);
else
result = dup(1);
} else
result = open((char *)fn, mode);
semihosting_errno = errno;
} else {
result = -1;
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);
result = close(fd);
semihosting_errno = errno;
}
break;
case 0x03: /* SYS_WRITEC */
{
unsigned char c;
retval = target_read_memory(target, r1, 1, 1, &c);
if (retval != ERROR_OK)
return retval;
putchar(c);
result = 0;
}
break;
case 0x04: /* SYS_WRITE0 */
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);
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);
uint8_t *buf = malloc(l);
if (!buf) {
result = -1;
semihosting_errno = ENOMEM;
} else {
retval = target_read_buffer(target, a, l, buf);
if (retval != ERROR_OK) {
free(buf);
return retval;
}
result = write(fd, buf, l);
semihosting_errno = errno;
if (result >= 0)
result = l - 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);
uint8_t *buf = malloc(l);
if (!buf) {
result = -1;
semihosting_errno = ENOMEM;
} else {
result = read(fd, buf, l);
semihosting_errno = errno;
if (result > 0) {
retval = target_write_buffer(target, a, result, buf);
if (retval != ERROR_OK) {
free(buf);
return retval;
}
result = l - result;
}
free(buf);
}
}
break;
case 0x07: /* SYS_READC */
result = getchar();
break;
case 0x08: /* SYS_ISERROR */
retval = target_read_memory(target, r1, 4, 1, params);
if (retval != ERROR_OK)
return retval;
result = (target_buffer_get_u32(target, params+0) != 0);
break;
case 0x09: /* SYS_ISTTY */
retval = target_read_memory(target, r1, 4, 1, params);
if (retval != ERROR_OK)
return retval;
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);
result = lseek(fd, pos, SEEK_SET);
semihosting_errno = errno;
if (result == pos)
result = 0;
}
break;
case 0x0c: /* SYS_FLEN */
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);
off_t cur = lseek(fd, 0, SEEK_CUR);
if (cur == (off_t)-1) {
semihosting_errno = errno;
result = -1;
break;
}
result = lseek(fd, 0, SEEK_END);
semihosting_errno = errno;
if (lseek(fd, cur, SEEK_SET) == (off_t)-1) {
semihosting_errno = errno;
result = -1;
}
}
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 (l <= 255) {
uint8_t fn[256];
retval = target_read_memory(target, a, 1, l, fn);
if (retval != ERROR_OK)
return retval;
fn[l] = 0;
result = remove((char *)fn);
semihosting_errno = errno;
} else {
result = -1;
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 (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;
result = rename((char *)fn1, (char *)fn2);
semihosting_errno = errno;
} else {
result = -1;
semihosting_errno = EINVAL;
}
}
break;
case 0x11: /* SYS_TIME */
result = time(NULL);
break;
case 0x13: /* SYS_ERRNO */
result = 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 = "foobar";
uint32_t s = strlen(arg) + 1;
if (l < s)
result = -1;
else {
retval = target_write_buffer(target, a, s, (void*)arg);
if (retval != ERROR_OK)
return retval;
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;
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", r1);
}
return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
case 0x0d: /* SYS_TMPNAM */
case 0x10: /* SYS_CLOCK */
case 0x12: /* SYS_SYSTEM */
case 0x17: /* angel_SWIreason_EnterSVC */
case 0x30: /* SYS_ELAPSED */
case 0x31: /* SYS_TICKFREQ */
default:
fprintf(stderr, "semihosting: unsupported call %#x\n", r0);
result = -1;
semihosting_errno = ENOTSUP;
}
/* resume execution to the original mode */
buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, result);
armv4_5->core_cache->reg_list[0].dirty = 1;
buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, lr);
armv4_5->core_cache->reg_list[15].dirty = 1;
buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, spsr);
armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1;
armv4_5->core_mode = spsr & 0x1f;
if (spsr & 0x20)
armv4_5->core_state = ARMV4_5_STATE_THUMB;
return target_resume(target, 1, 0, 0, 0);
}
/**
* Checks for and processes an ARM semihosting request. This is meant
* to be called when the target is stopped due to a debug mode entry.
* If the value 0 is returned then there was nothing to process. A non-zero
* return value signifies that a request was processed and the target resumed,
* or an error was encountered, in which case the caller must return
* immediately.
*
* @param target Pointer to the ARM target to process
* @param retval Pointer to a location where the return code will be stored
* @return non-zero value if a request was processed or an error encountered
*/
int arm_semihosting(struct target *target, int *retval)
{
struct arm *armv4_5 = target_to_armv4_5(target);
uint32_t lr, spsr;
if (!semihosting_active ||
armv4_5->core_mode != ARMV4_5_MODE_SVC ||
buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32) != 0x08)
return 0;
lr = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, ARMV4_5_MODE_SVC, 14).value, 0, 32);
spsr = buf_get_u32(armv4_5->spsr->value, 0, 32);
/* check instruction that triggered this trap */
if (spsr & (1 << 5)) {
/* was in Thumb mode */
uint8_t insn_buf[2];
uint16_t insn;
*retval = target_read_memory(target, lr-2, 2, 1, insn_buf);
if (*retval != ERROR_OK)
return 1;
insn = target_buffer_get_u16(target, insn_buf);
if (insn != 0xDFAB)
return 0;
} else {
/* was in ARM mode */
uint8_t insn_buf[4];
uint32_t insn;
*retval = target_read_memory(target, lr-4, 4, 1, insn_buf);
if (*retval != ERROR_OK)
return 1;
insn = target_buffer_get_u32(target, insn_buf);
if (insn != 0xEF123456)
return 0;
}
*retval = do_semihosting(target);
return 1;
}

View File

@ -0,0 +1,28 @@
/***************************************************************************
* 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef ARM_SEMIHOSTING_H
#define ARM_SEMIHOSTING_H
extern int semihosting_active;
int arm_semihosting(struct target *target, int *retval);
#endif