X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Ftarget%2Farm_semihosting.c;h=115c7d4e498b44f2fc75b974b57b3d7e66e5d854;hp=4788686296f265f333f8145ef73d15aef5c5c398;hb=374127301ec1d72033b9d573b72c7abdfd61990d;hpb=bdde9460b923ab61fad678bf1e3f0da04e1d94ee diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c index 4788686296..115c7d4e49 100644 --- a/src/target/arm_semihosting.c +++ b/src/target/arm_semihosting.c @@ -2,6 +2,9 @@ * Copyright (C) 2009 by Marvell Technology Group Ltd. * * Written by Nicolas Pitre * * * + * Copyright (C) 2010 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * * 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,20 +37,37 @@ #include "config.h" #endif +#include "arm.h" #include "armv4_5.h" +#include "arm7_9_common.h" +#include "armv7m.h" +#include "cortex_m.h" #include "register.h" #include "arm_semihosting.h" #include #include +#include +static 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 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);; + struct arm *arm = target_to_arm(target); + 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, result; @@ -56,6 +76,9 @@ static int do_semihosting(struct target *target) * - 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: explore mapping requests to GDB's "File-I/O Remote + * Protocol Extension" ... when GDB is active. */ switch (r0) { case 0x01: /* SYS_OPEN */ @@ -68,32 +91,25 @@ static int do_semihosting(struct target *target) 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); + if (m < 4) + result = dup(STDIN_FILENO); else - result = dup(1); - } else - result = open((char *)fn, mode); - armv4_5->semihosting_errno = errno; + result = dup(STDOUT_FILENO); + } else { + /* cygwin requires the permission setting + * otherwise it will fail to reopen a previously + * written file */ + result = open((char *)fn, open_modeflags[m], 0644); + } + arm->semihosting_errno = errno; } else { result = -1; - armv4_5->semihosting_errno = EINVAL; + arm->semihosting_errno = EINVAL; } } break; @@ -105,7 +121,7 @@ static int do_semihosting(struct target *target) else { int fd = target_buffer_get_u32(target, params+0); result = close(fd); - armv4_5->semihosting_errno = errno; + arm->semihosting_errno = errno; } break; @@ -123,7 +139,7 @@ static int do_semihosting(struct target *target) case 0x04: /* SYS_WRITE0 */ do { unsigned char c; - retval = target_read_memory(target, r1, 1, 1, &c); + retval = target_read_memory(target, r1++, 1, 1, &c); if (retval != ERROR_OK) return retval; if (!c) @@ -144,7 +160,7 @@ static int do_semihosting(struct target *target) uint8_t *buf = malloc(l); if (!buf) { result = -1; - armv4_5->semihosting_errno = ENOMEM; + arm->semihosting_errno = ENOMEM; } else { retval = target_read_buffer(target, a, l, buf); if (retval != ERROR_OK) { @@ -152,7 +168,7 @@ static int do_semihosting(struct target *target) return retval; } result = write(fd, buf, l); - armv4_5->semihosting_errno = errno; + arm->semihosting_errno = errno; if (result >= 0) result = l - result; free(buf); @@ -171,11 +187,11 @@ static int do_semihosting(struct target *target) uint8_t *buf = malloc(l); if (!buf) { result = -1; - armv4_5->semihosting_errno = ENOMEM; + arm->semihosting_errno = ENOMEM; } else { result = read(fd, buf, l); - armv4_5->semihosting_errno = errno; - if (result > 0) { + arm->semihosting_errno = errno; + if (result >= 0) { retval = target_write_buffer(target, a, result, buf); if (retval != ERROR_OK) { free(buf); @@ -214,7 +230,7 @@ static int do_semihosting(struct target *target) 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); - armv4_5->semihosting_errno = errno; + arm->semihosting_errno = errno; if (result == pos) result = 0; } @@ -226,18 +242,14 @@ static int do_semihosting(struct target *target) 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) { - armv4_5->semihosting_errno = errno; + struct stat buf; + result = fstat(fd, &buf); + if (result == -1) { + arm->semihosting_errno = errno; result = -1; break; } - result = lseek(fd, 0, SEEK_END); - armv4_5->semihosting_errno = errno; - if (lseek(fd, cur, SEEK_SET) == (off_t)-1) { - armv4_5->semihosting_errno = errno; - result = -1; - } + result = buf.st_size; } break; @@ -255,10 +267,10 @@ static int do_semihosting(struct target *target) return retval; fn[l] = 0; result = remove((char *)fn); - armv4_5->semihosting_errno = errno; + arm->semihosting_errno = errno; } else { result = -1; - armv4_5->semihosting_errno = EINVAL; + arm->semihosting_errno = EINVAL; } } break; @@ -283,10 +295,10 @@ static int do_semihosting(struct target *target) fn1[l1] = 0; fn2[l2] = 0; result = rename((char *)fn1, (char *)fn2); - armv4_5->semihosting_errno = errno; + arm->semihosting_errno = errno; } else { result = -1; - armv4_5->semihosting_errno = EINVAL; + arm->semihosting_errno = EINVAL; } } break; @@ -296,7 +308,7 @@ static int do_semihosting(struct target *target) break; case 0x13: /* SYS_ERRNO */ - result = armv4_5->semihosting_errno; + result = arm->semihosting_errno; break; case 0x15: /* SYS_GET_CMDLINE */ @@ -311,7 +323,7 @@ static int do_semihosting(struct target *target) if (l < s) result = -1; else { - retval = target_write_buffer(target, a, s, (void*)arg); + retval = target_write_buffer(target, a, s, (void *)arg); if (retval != ERROR_OK) return retval; result = 0; @@ -372,19 +384,48 @@ static int do_semihosting(struct target *target) fprintf(stderr, "semihosting: unsupported call %#x\n", (unsigned) r0); result = -1; - armv4_5->semihosting_errno = ENOTSUP; + arm->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; + + /* REVISIT this looks wrong ... ARM11 and Cortex-A8 + * should work this way at least sometimes. + */ + if (is_arm7_9(target_to_arm7_9(target))) { + uint32_t spsr; + + /* return value in R0 */ + buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, result); + arm->core_cache->reg_list[0].dirty = 1; + + /* 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 = 1; + + /* 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 = 1; + arm->core_mode = spsr & 0x1f; + if (spsr & 0x20) + arm->core_state = ARM_STATE_THUMB; + + } 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, result); + arm->core_cache->reg_list[0].dirty = 1; + } + return target_resume(target, 1, 0, 0, 0); } @@ -396,44 +437,95 @@ 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); + uint32_t pc, lr, spsr; + struct reg *r; - if (!armv4_5->is_semihosting || - armv4_5->core_mode != ARMV4_5_MODE_SVC || - buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32) != 0x08) + if (!arm->is_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 (is_arm7_9(target_to_arm7_9(target))) { + if (arm->core_mode != ARM_MODE_SVC) + return 0; - /* 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) + /* Check for PC == 0x00000008 or 0xffff0008: Supervisor Call vector. */ + r = arm->pc; + pc = buf_get_u32(r->value, 0, 32); + if (pc != 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; - insn = target_buffer_get_u16(target, insn_buf); - if (insn != 0xDFAB) + } + + 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); + } 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); + + /* SVC 0x123456 */ + if (insn != 0xEF123456) + return 0; + } + } else if (is_armv7m(target_to_armv7m(target))) { + uint16_t insn; + + 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_u32(target, insn_buf); - if (insn != 0xEF123456) + + /* bkpt 0xAB */ + if (insn != 0xBEAB) return 0; + } else { + LOG_ERROR("Unsupported semi-hosting Target"); + return 0; } *retval = do_semihosting(target);