X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Ftarget%2Farm_semihosting.c;h=61f1e7801f443b5daa3044f0db491a3bc681c3f1;hp=5e0a2bedcd5c8520e0582797aec2694de505d50d;hb=0118dc5e47da98cfd082d1b109ed995dcb779510;hpb=ed59dfc80aa6fc48a0894c8e46cee675f38ac949 diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c index 5e0a2bedcd..61f1e7801f 100644 --- a/src/target/arm_semihosting.c +++ b/src/target/arm_semihosting.c @@ -2,6 +2,15 @@ * Copyright (C) 2009 by Marvell Technology Group Ltd. * * Written by Nicolas Pitre * * * + * Copyright (C) 2010 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * Copyright (C) 2016 by Square, Inc. * + * Steven Stallion * + * * + * Copyright (C) 2018 by Liviu Ionescu * + * * + * * * 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 * @@ -13,9 +22,7 @@ * 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. * + * along with this program. If not, see . * ***************************************************************************/ /** @@ -34,359 +41,114 @@ #include "config.h" #endif +#include "arm.h" #include "armv4_5.h" +#include "arm7_9_common.h" +#include "armv7m.h" +#include "armv7a.h" +#include "armv8.h" +#include "cortex_m.h" #include "register.h" +#include "arm_opcodes.h" +#include "target_type.h" #include "arm_semihosting.h" #include #include +#include -/* TODO: this needs to be per target */ -int semihosting_active; -int semihosting_errno; - -static int do_semihosting(struct target *target) +static int arm_semihosting_resume(struct target *target, int *retval) { - 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; + if (is_armv8(target_to_armv8(target))) { + struct armv8_common *armv8 = target_to_armv8(target); + if (armv8->last_run_control_op == ARMV8_RUNCONTROL_RESUME) { + *retval = target_resume(target, 1, 0, 0, 0); + if (*retval != ERROR_OK) { + LOG_ERROR("Failed to resume target"); + return 0; } + } else if (armv8->last_run_control_op == ARMV8_RUNCONTROL_STEP) + target->debug_reason = DBG_REASON_SINGLESTEP; + } else { + *retval = target_resume(target, 1, 0, 0, 0); + if (*retval != ERROR_OK) { + LOG_ERROR("Failed to resume target"); + return 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 1; +} + +static int post_result(struct target *target) +{ + struct arm *arm = target_to_arm(target); + + if (!target->semihosting) + return ERROR_FAIL; + + /* REVISIT this looks wrong ... ARM11 and Cortex-A8 + * should work this way at least sometimes. + */ + if (is_arm7_9(target_to_arm7_9(target)) || + is_armv7a(target_to_armv7a(target))) { + uint32_t spsr; + + /* return value in R0 */ + buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result); + arm->core_cache->reg_list[0].dirty = true; + + /* LR --> PC */ + buf_set_u32(arm->core_cache->reg_list[15].value, 0, 32, + buf_get_u32(arm_reg_current(arm, 14)->value, 0, 32)); + arm->core_cache->reg_list[15].dirty = true; + + /* saved PSR --> current PSR */ + spsr = buf_get_u32(arm->spsr->value, 0, 32); + + /* REVISIT should this be arm_set_cpsr(arm, spsr) + * instead of a partially unrolled version? + */ + + buf_set_u32(arm->cpsr->value, 0, 32, spsr); + arm->cpsr->dirty = true; + arm->core_mode = spsr & 0x1f; + if (spsr & 0x20) + arm->core_state = ARM_STATE_THUMB; + + } else if (is_armv8(target_to_armv8(target))) { + if (arm->core_state == ARM_STATE_AARCH64) { + /* return value in R0 */ + buf_set_u64(arm->core_cache->reg_list[0].value, 0, 64, target->semihosting->result); + arm->core_cache->reg_list[0].dirty = true; + + uint64_t pc = buf_get_u64(arm->core_cache->reg_list[32].value, 0, 64); + buf_set_u64(arm->pc->value, 0, 64, pc + 4); + arm->pc->dirty = true; } - 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; + } else { + /* resume execution, this will be pc+2 to skip over the + * bkpt instruction */ + + /* return result in R0 */ + buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result); + arm->core_cache->reg_list[0].dirty = true; } - /* 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); + return ERROR_OK; +} + +/** + * Initialize ARM semihosting support. + * + * @param target Pointer to the ARM target to initialize. + * @return An error status if there is a problem during initialization. + */ +int arm_semihosting_init(struct target *target) +{ + struct arm *arm = target_to_arm(target); + assert(arm->setup_semihosting); + semihosting_common_init(target, arm->setup_semihosting, post_result); + + return ERROR_OK; } /** @@ -397,46 +159,176 @@ static int do_semihosting(struct target *target) * or an error was encountered, in which case the caller must return * immediately. * - * @param target Pointer to the ARM target to process + * @param target Pointer to the ARM target to process. This target must + * not represent an ARMv6-M or ARMv7-M processor. * @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; + struct arm *arm = target_to_arm(target); + struct armv7a_common *armv7a = target_to_armv7a(target); + uint32_t pc, lr, spsr; + struct reg *r; - 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) + struct semihosting *semihosting = target->semihosting; + if (!semihosting) 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); + if (!semihosting->is_active) + return 0; + + if (is_arm7_9(target_to_arm7_9(target)) || + is_armv7a(armv7a)) { + uint32_t vbar = 0x00000000; + + if (arm->core_mode != ARM_MODE_SVC) + return 0; + + if (is_armv7a(armv7a)) { + struct arm_dpm *dpm = armv7a->arm.dpm; + + *retval = dpm->prepare(dpm); + if (*retval == ERROR_OK) { + *retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 0, 0, 12, 0, 0), + &vbar); + + dpm->finish(dpm); + + if (*retval != ERROR_OK) + return 1; + } else { + return 1; + } + } + + /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */ + r = arm->pc; + pc = buf_get_u32(r->value, 0, 32); + if (pc != (vbar + 0x00000008) && pc != 0xffff0008) + return 0; + + r = arm_reg_current(arm, 14); + lr = buf_get_u32(r->value, 0, 32); + + /* Core-specific code should make sure SPSR is retrieved + * when the above checks pass... + */ + if (!arm->spsr->valid) { + LOG_ERROR("SPSR not valid!"); + *retval = ERROR_FAIL; + return 1; + } + + spsr = buf_get_u32(arm->spsr->value, 0, 32); + + /* check instruction that triggered this trap */ + if (spsr & (1 << 5)) { + /* was in Thumb (or ThumbEE) 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); + + /* SVC 0xab */ + if (insn != 0xDFAB) + return 0; + } else if (spsr & (1 << 24)) { + /* was in Jazelle mode */ + 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); - /* check instruction that triggered this trap */ - if (spsr & (1 << 5)) { - /* was in Thumb mode */ - uint8_t insn_buf[2]; + /* SVC 0x123456 */ + if (insn != 0xEF123456) + return 0; + } + } else if (is_armv7m(target_to_armv7m(target))) { uint16_t insn; - *retval = target_read_memory(target, lr-2, 2, 1, insn_buf); + + if (target->debug_reason != DBG_REASON_BREAKPOINT) + return 0; + + r = arm->pc; + pc = buf_get_u32(r->value, 0, 32); + + pc &= ~1; + *retval = target_read_u16(target, pc, &insn); if (*retval != ERROR_OK) return 1; - insn = target_buffer_get_u16(target, insn_buf); - if (insn != 0xDFAB) + + /* bkpt 0xAB */ + if (insn != 0xBEAB) 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) + } else if (is_armv8(target_to_armv8(target))) { + if (target->debug_reason != DBG_REASON_BREAKPOINT) + return 0; + + if (arm->core_state == ARM_STATE_AARCH64) { + uint32_t insn = 0; + r = arm->pc; + uint64_t pc64 = buf_get_u64(r->value, 0, 64); + *retval = target_read_u32(target, pc64, &insn); + + if (*retval != ERROR_OK) + return 1; + + /* bkpt 0xAB */ + if (insn != 0xD45E0000) + return 0; + } else return 1; - insn = target_buffer_get_u32(target, insn_buf); - if (insn != 0xEF123456) + } else { + LOG_ERROR("Unsupported semi-hosting Target"); + return 0; + } + + /* Perform semihosting if we are not waiting on a fileio + * operation to complete. + */ + if (!semihosting->hit_fileio) { + if (is_armv8(target_to_armv8(target)) && + arm->core_state == ARM_STATE_AARCH64) { + /* Read op and param from register x0 and x1 respectively. */ + semihosting->op = buf_get_u64(arm->core_cache->reg_list[0].value, 0, 64); + semihosting->param = buf_get_u64(arm->core_cache->reg_list[1].value, 0, 64); + semihosting->word_size_bytes = 8; + } else { + /* Read op and param from register r0 and r1 respectively. */ + semihosting->op = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32); + semihosting->param = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32); + 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; + } } - *retval = do_semihosting(target); - return 1; + /* Resume if target it is resumable and we are not waiting on a fileio + * operation to complete: + */ + if (semihosting->is_resumable && !semihosting->hit_fileio) + return arm_semihosting_resume(target, retval); + + return 0; }