diff options
Diffstat (limited to 'qemu/target-arm/arm-semi.c')
-rw-r--r-- | qemu/target-arm/arm-semi.c | 180 |
1 files changed, 130 insertions, 50 deletions
diff --git a/qemu/target-arm/arm-semi.c b/qemu/target-arm/arm-semi.c index a2a736956..8be0645eb 100644 --- a/qemu/target-arm/arm-semi.c +++ b/qemu/target-arm/arm-semi.c @@ -18,13 +18,7 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <time.h> +#include "qemu/osdep.h" #include "cpu.h" #include "exec/semihost.h" @@ -36,6 +30,7 @@ #include "qemu-common.h" #include "exec/gdbstub.h" #include "hw/arm/arm.h" +#include "qemu/cutils.h" #endif #define TARGET_SYS_OPEN 0x01 @@ -58,6 +53,7 @@ #define TARGET_SYS_GET_CMDLINE 0x15 #define TARGET_SYS_HEAPINFO 0x16 #define TARGET_SYS_EXIT 0x18 +#define TARGET_SYS_SYNCCACHE 0x19 /* ADP_Stopped_ApplicationExit is used for exit(0), * anything else is implemented as exit(1) */ @@ -134,6 +130,7 @@ static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) #ifdef CONFIG_USER_ONLY TaskState *ts = cs->opaque; #endif + target_ulong reg0 = is_a64(env) ? env->xregs[0] : env->regs[0]; if (ret == (target_ulong)-1) { #ifdef CONFIG_USER_ONLY @@ -141,22 +138,46 @@ static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) #else syscall_err = err; #endif - env->regs[0] = ret; + reg0 = ret; } else { /* Fixup syscalls that use nonstardard return conventions. */ - switch (env->regs[0]) { + switch (reg0) { case TARGET_SYS_WRITE: case TARGET_SYS_READ: - env->regs[0] = arm_semi_syscall_len - ret; + reg0 = arm_semi_syscall_len - ret; break; case TARGET_SYS_SEEK: - env->regs[0] = 0; + reg0 = 0; break; default: - env->regs[0] = ret; + reg0 = ret; break; } } + if (is_a64(env)) { + env->xregs[0] = reg0; + } else { + env->regs[0] = reg0; + } +} + +static target_ulong arm_flen_buf(ARMCPU *cpu) +{ + /* Return an address in target memory of 64 bytes where the remote + * gdb should write its stat struct. (The format of this structure + * is defined by GDB's remote protocol and is not target-specific.) + * We put this on the guest's stack just below SP. + */ + CPUARMState *env = &cpu->env; + target_ulong sp; + + if (is_a64(env)) { + sp = env->xregs[31]; + } else { + sp = env->regs[13]; + } + + return sp - 64; } static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) @@ -166,8 +187,13 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) /* The size is always stored in big-endian order, extract the value. We assume the size always fit in 32 bits. */ uint32_t size; - cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0); - env->regs[0] = be32_to_cpu(size); + cpu_memory_rw_debug(cs, arm_flen_buf(cpu) + 32, (uint8_t *)&size, 4, 0); + size = be32_to_cpu(size); + if (is_a64(env)) { + env->xregs[0] = size; + } else { + env->regs[0] = size; + } #ifdef CONFIG_USER_ONLY ((TaskState *)cs->opaque)->swi_errno = err; #else @@ -175,17 +201,46 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) #endif } +static target_ulong arm_gdb_syscall(ARMCPU *cpu, gdb_syscall_complete_cb cb, + const char *fmt, ...) +{ + va_list va; + CPUARMState *env = &cpu->env; + + va_start(va, fmt); + gdb_do_syscallv(cb, fmt, va); + va_end(va); + + /* FIXME: we are implicitly relying on the syscall completing + * before this point, which is not guaranteed. We should + * put in an explicit synchronization between this and + * the callback function. + */ + + return is_a64(env) ? env->xregs[0] : env->regs[0]; +} + /* Read the input value from the argument block; fail the semihosting * call if the memory read fails. */ #define GET_ARG(n) do { \ - if (get_user_ual(arg ## n, args + (n) * 4)) { \ - return (uint32_t)-1; \ + if (is_a64(env)) { \ + if (get_user_u64(arg ## n, args + (n) * 8)) { \ + return -1; \ + } \ + } else { \ + if (get_user_u32(arg ## n, args + (n) * 4)) { \ + return -1; \ + } \ } \ } while (0) -#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4) -uint32_t do_arm_semihosting(CPUARMState *env) +#define SET_ARG(n, val) \ + (is_a64(env) ? \ + put_user_u64(val, args + (n) * 8) : \ + put_user_u32(val, args + (n) * 4)) + +target_ulong do_arm_semihosting(CPUARMState *env) { ARMCPU *cpu = arm_env_get_cpu(env); CPUState *cs = CPU(cpu); @@ -201,8 +256,15 @@ uint32_t do_arm_semihosting(CPUARMState *env) CPUARMState *ts = env; #endif - nr = env->regs[0]; - args = env->regs[1]; + if (is_a64(env)) { + /* Note that the syscall number is in W0, not X0 */ + nr = env->xregs[0] & 0xffffffffU; + args = env->xregs[1]; + } else { + nr = env->regs[0]; + args = env->regs[1]; + } + switch (nr) { case TARGET_SYS_OPEN: GET_ARG(0); @@ -223,9 +285,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) return result_fileno; } if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0, - (int)arg2+1, gdb_open_modeflags[arg1]); - ret = env->regs[0]; + ret = arm_gdb_syscall(cpu, arm_semi_cb, "open,%s,%x,1a4", arg0, + (int)arg2+1, gdb_open_modeflags[arg1]); } else { ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644)); } @@ -234,8 +295,7 @@ uint32_t do_arm_semihosting(CPUARMState *env) case TARGET_SYS_CLOSE: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "close,%x", arg0); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", arg0); } else { return set_swi_errno(ts, close(arg0)); } @@ -248,8 +308,7 @@ uint32_t do_arm_semihosting(CPUARMState *env) return (uint32_t)-1; /* Write to debug console. stderr is near enough. */ if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "write,2,%x,1", args); } else { return write(STDERR_FILENO, &c, 1); } @@ -260,8 +319,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) return (uint32_t)-1; len = strlen(s); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len); - ret = env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "write,2,%x,%x", + args, len); } else { ret = write(STDERR_FILENO, s, len); } @@ -274,8 +333,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) len = arg2; if (use_gdb_syscalls()) { arm_semi_syscall_len = len; - gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "write,%x,%x,%x", + arg0, arg1, len); } else { s = lock_user(VERIFY_READ, arg1, len, 1); if (!s) { @@ -295,8 +354,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) len = arg2; if (use_gdb_syscalls()) { arm_semi_syscall_len = len; - gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "read,%x,%x,%x", + arg0, arg1, len); } else { s = lock_user(VERIFY_WRITE, arg1, len, 0); if (!s) { @@ -317,8 +376,7 @@ uint32_t do_arm_semihosting(CPUARMState *env) case TARGET_SYS_ISTTY: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "isatty,%x", arg0); } else { return isatty(arg0); } @@ -326,8 +384,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0", + arg0, arg1); } else { ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET)); if (ret == (uint32_t)-1) @@ -337,9 +395,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) case TARGET_SYS_FLEN: GET_ARG(0); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x", - arg0, env->regs[13]-64); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_flen_cb, "fstat,%x,%x", + arg0, arm_flen_buf(cpu)); } else { struct stat buf; ret = set_swi_errno(ts, fstat(arg0, &buf)); @@ -354,8 +411,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1); - ret = env->regs[0]; + ret = arm_gdb_syscall(cpu, arm_semi_cb, "unlink,%s", + arg0, (int)arg1+1); } else { s = lock_user_string(arg0); if (!s) { @@ -372,9 +429,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(2); GET_ARG(3); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "rename,%s,%s", - arg0, (int)arg1+1, arg2, (int)arg3+1); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "rename,%s,%s", + arg0, (int)arg1+1, arg2, (int)arg3+1); } else { char *s2; s = lock_user_string(arg0); @@ -398,8 +454,8 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(0); GET_ARG(1); if (use_gdb_syscalls()) { - gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1); - return env->regs[0]; + return arm_gdb_syscall(cpu, arm_semi_cb, "system,%s", + arg0, (int)arg1+1); } else { s = lock_user_string(arg0); if (!s) { @@ -558,11 +614,35 @@ uint32_t do_arm_semihosting(CPUARMState *env) return 0; } case TARGET_SYS_EXIT: - /* ARM specifies only Stopped_ApplicationExit as normal - * exit, everything else is considered an error */ - ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1; + if (is_a64(env)) { + /* The A64 version of this call takes a parameter block, + * so the application-exit type can return a subcode which + * is the exit status code from the application. + */ + GET_ARG(0); + GET_ARG(1); + + if (arg0 == ADP_Stopped_ApplicationExit) { + ret = arg1; + } else { + ret = 1; + } + } else { + /* ARM specifies only Stopped_ApplicationExit as normal + * exit, everything else is considered an error */ + ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1; + } gdb_exit(env, ret); exit(ret); + case TARGET_SYS_SYNCCACHE: + /* Clean the D-cache and invalidate the I-cache for the specified + * virtual address range. This is a nop for us since we don't + * implement caches. This is only present on A64. + */ + if (is_a64(env)) { + return 0; + } + /* fall through -- invalid for A32/T32 */ default: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); cpu_dump_state(cs, stderr, fprintf, 0); |