This is for full-system only; not implemented in user mode Written by Derek Hower.
Signed-off-by: Christopher Covington <c...@codeaurora.org> --- include/exec/softmmu-semi.h | 21 ++++++- target-arm/arm-semi.c | 142 ++++++++++++++++++++++++++++++++++++-------- target-arm/cpu.h | 3 +- target-arm/helper-a64.c | 28 ++++++++- target-arm/helper.c | 1 + target-arm/internals.h | 8 +++ target-arm/translate-a64.c | 2 +- 7 files changed, 174 insertions(+), 31 deletions(-) diff --git a/include/exec/softmmu-semi.h b/include/exec/softmmu-semi.h index 8401f7d..9ab8353 100644 --- a/include/exec/softmmu-semi.h +++ b/include/exec/softmmu-semi.h @@ -9,6 +9,13 @@ #ifndef SOFTMMU_SEMI_H #define SOFTMMU_SEMI_H 1 +static inline uint64_t softmmu_tget64(CPUArchState *env, uint64_t addr) +{ + uint64_t val; + + cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 0); + return tswap64(val); +} static inline uint32_t softmmu_tget32(CPUArchState *env, uint32_t addr) { uint32_t val; @@ -24,19 +31,27 @@ static inline uint32_t softmmu_tget8(CPUArchState *env, uint32_t addr) return val; } +#define get_user_u64(arg, p) ({ arg = softmmu_tget64(env, p) ; 0; }) #define get_user_u32(arg, p) ({ arg = softmmu_tget32(env, p) ; 0; }) #define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; }) #define get_user_ual(arg, p) get_user_u32(arg, p) +static inline void softmmu_tput64(CPUArchState *env, uint64_t addr, uint64_t val) +{ + val = tswap64(val); + cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 8, 1); +} + static inline void softmmu_tput32(CPUArchState *env, uint32_t addr, uint32_t val) { val = tswap32(val); cpu_memory_rw_debug(ENV_GET_CPU(env), addr, (uint8_t *)&val, 4, 1); } +#define put_user_u64(arg, p) ({ softmmu_tput64(env, p, arg) ; 0; }) #define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; }) #define put_user_ual(arg, p) put_user_u32(arg, p) -static void *softmmu_lock_user(CPUArchState *env, uint32_t addr, uint32_t len, +static void *softmmu_lock_user(CPUArchState *env, target_ulong addr, uint32_t len, int copy) { uint8_t *p; @@ -48,11 +63,11 @@ static void *softmmu_lock_user(CPUArchState *env, uint32_t addr, uint32_t len, return p; } #define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy) -static char *softmmu_lock_user_string(CPUArchState *env, uint32_t addr) +static char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) { char *p; char *s; - uint8_t c; + uint8_t c = 0; /* TODO: Make this something that isn't fixed size. */ s = p = malloc(1024); if (!s) { diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c index bcc70ec..b89ea8f 100644 --- a/target-arm/arm-semi.c +++ b/target-arm/arm-semi.c @@ -4,6 +4,9 @@ * Copyright (c) 2005, 2007 CodeSourcery. * Written by Paul Brook. * + * Copyright (c) 2015 the Linux Foundation. + * Written by Derek Hower. + * * 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 @@ -140,19 +143,35 @@ static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) #else syscall_err = err; #endif - env->regs[0] = ret; + if (env->aarch64) { + env->xregs[0] = ret; + } else { + env->regs[0] = ret; + } } else { /* Fixup syscalls that use nonstardard return conventions. */ switch (env->regs[0]) { case TARGET_SYS_WRITE: case TARGET_SYS_READ: + if (env->aarch64) { + env->xregs[0] = arm_semi_syscall_len - ret; + } else { env->regs[0] = arm_semi_syscall_len - ret; + } break; case TARGET_SYS_SEEK: + if (env->aarch64) { + env->xregs[0] = 0; + } else { env->regs[0] = 0; + } break; default: + if (env->aarch64) { + env->xregs[0] = ret; + } else { env->regs[0] = ret; + } break; } } @@ -165,8 +184,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); + if (env->aarch64) { + cpu_memory_rw_debug(cs, env->pc-64+32, (uint8_t *)&size, 4, 0); + env->xregs[0] = be32_to_cpu(size); + } else { + cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0); + env->regs[0] = be32_to_cpu(size); + } #ifdef CONFIG_USER_ONLY ((TaskState *)cs->opaque)->swi_errno = err; #else @@ -177,14 +201,28 @@ static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err) /* 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; \ +#define GET_ARG(n) do { \ + if (env->aarch64) { \ + if (get_user_u64(arg ## n, args + (n) * 8)) { \ + return (target_ulong)-1; \ + } \ + } else { \ + if (get_user_ual(arg ## n, (uint32_t) args + (n) * 4)) { \ + return (target_ulong)-1; \ + } \ + } \ + } while (0) + +#define SET_ARG(n, val) \ + ({ \ + if (env->aarch64) { \ + ret = put_user_u64(val, args + (n) * 8); \ + } else { \ + ret = put_user_ual(val, args + (n) * 4); \ } \ -} while (0) - -#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4) -uint32_t do_arm_semihosting(CPUARMState *env) + 0; \ + }) +target_ulong do_arm_semihosting(CPUARMState *env) { target_ulong args; target_ulong arg0, arg1, arg2, arg3; @@ -200,8 +238,13 @@ uint32_t do_arm_semihosting(CPUARMState *env) CPUARMState *ts = env; #endif - nr = env->regs[0]; - args = env->regs[1]; + if (env->aarch64) { + nr = env->xregs[0]; + args = env->xregs[1]; + } else { + nr = env->regs[0]; + args = env->regs[1]; + } switch (nr) { case TARGET_SYS_OPEN: GET_ARG(0); @@ -224,9 +267,13 @@ uint32_t do_arm_semihosting(CPUARMState *env) 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]; + if (env->aarch64) { + ret = env->xregs[0]; + } else { + ret = env->regs[0]; + } } else { - ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644)); + ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644)); } unlock_user(s, arg0, 0); return ret; @@ -234,7 +281,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(0); if (use_gdb_syscalls()) { gdb_do_syscall(arm_semi_cb, "close,%x", arg0); - return env->regs[0]; + if (env->aarch64) { + return env->xregs[0]; + } else { + return env->regs[0]; + } } else { return set_swi_errno(ts, close(arg0)); } @@ -248,7 +299,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) /* 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]; + if (env->aarch64) { + return env->xregs[0]; + } else { + return env->regs[0]; + } } else { return write(STDERR_FILENO, &c, 1); } @@ -260,7 +315,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) len = strlen(s); if (use_gdb_syscalls()) { gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len); - ret = env->regs[0]; + if (env->aarch64) { + ret = env->xregs[0]; + } else { + ret = env->regs[0]; + } } else { ret = write(STDERR_FILENO, s, len); } @@ -274,7 +333,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) 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]; + if (env->aarch64) { + return env->xregs[0]; + } else { + return env->regs[0]; + } } else { s = lock_user(VERIFY_READ, arg1, len, 1); if (!s) { @@ -295,7 +358,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) 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]; + if (env->aarch64) { + return env->xregs[0]; + } else { + return env->regs[0]; + } } else { s = lock_user(VERIFY_WRITE, arg1, len, 0); if (!s) { @@ -317,7 +384,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(0); if (use_gdb_syscalls()) { gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0); - return env->regs[0]; + if (env->aarch64) { + return env->xregs[0]; + } else { + return env->regs[0]; + } } else { return isatty(arg0); } @@ -326,7 +397,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(1); if (use_gdb_syscalls()) { gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1); - return env->regs[0]; + if (env->aarch64) { + return env->xregs[0]; + } else { + return env->regs[0]; + } } else { ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET)); if (ret == (uint32_t)-1) @@ -336,9 +411,16 @@ uint32_t do_arm_semihosting(CPUARMState *env) case TARGET_SYS_FLEN: GET_ARG(0); if (use_gdb_syscalls()) { + if (env->aarch64) { + gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x", + arg0, env->pc-64); + return env->xregs[0]; + + } else { gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x", arg0, env->regs[13]-64); return env->regs[0]; + } } else { struct stat buf; ret = set_swi_errno(ts, fstat(arg0, &buf)); @@ -354,7 +436,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(1); if (use_gdb_syscalls()) { gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1); - ret = env->regs[0]; + if (env->aarch64) { + ret = env->xregs[0]; + } else { + ret = env->regs[0]; + } } else { s = lock_user_string(arg0); if (!s) { @@ -373,7 +459,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) 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]; + if (env->aarch64) { + return env->xregs[0]; + } else { + return env->regs[0]; + } } else { char *s2; s = lock_user_string(arg0); @@ -398,7 +488,11 @@ uint32_t do_arm_semihosting(CPUARMState *env) GET_ARG(1); if (use_gdb_syscalls()) { gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1); - return env->regs[0]; + if (env->aarch64) { + return env->xregs[0]; + } else { + return env->regs[0]; + } } else { s = lock_user_string(arg0); if (!s) { diff --git a/target-arm/cpu.h b/target-arm/cpu.h index d4a5899..2525569 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -56,6 +56,7 @@ #define EXCP_SMC 13 /* Secure Monitor Call */ #define EXCP_VIRQ 14 #define EXCP_VFIQ 15 +#define EXCP_ARMV8_HLT 16 /* avoid conflict with cpu-defs.h:EXCP_HLT */ #define ARMV7M_EXCP_RESET 1 #define ARMV7M_EXCP_NMI 2 @@ -489,7 +490,7 @@ typedef struct CPUARMState { ARMCPU *cpu_arm_init(const char *cpu_model); int cpu_arm_exec(CPUARMState *s); -uint32_t do_arm_semihosting(CPUARMState *env); +target_ulong do_arm_semihosting(CPUARMState *env); void aarch64_sync_32_to_64(CPUARMState *env); void aarch64_sync_64_to_32(CPUARMState *env); diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c index 861f6fa..8803293 100644 --- a/target-arm/helper-a64.c +++ b/target-arm/helper-a64.c @@ -466,6 +466,14 @@ void aarch64_cpu_do_interrupt(CPUState *cs) unsigned int new_el = arm_excp_target_el(cs, cs->exception_index); target_ulong addr = env->cp15.vbar_el[new_el]; unsigned int new_mode = aarch64_pstate_mode(new_el, true); +#ifndef CONFIG_USER_ONLY + uint64_t mask; +#endif + + uint32_t syndrome = + cs->exception_index == EXCP_ARMV8_HLT ? + env->exception.syndrome & ~0xffff : + env->exception.syndrome; if (arm_current_el(env) < new_el) { if (env->aarch64) { @@ -482,7 +490,7 @@ void aarch64_cpu_do_interrupt(CPUState *cs) if (qemu_loglevel_mask(CPU_LOG_INT) && !excp_is_internal(cs->exception_index)) { qemu_log_mask(CPU_LOG_INT, "...with ESR 0x%" PRIx32 "\n", - env->exception.syndrome); + syndrome); } if (arm_is_psci_call(cpu, cs->exception_index)) { @@ -504,8 +512,24 @@ void aarch64_cpu_do_interrupt(CPUState *cs) case EXCP_HVC: case EXCP_HYP_TRAP: case EXCP_SMC: - env->cp15.esr_el[new_el] = env->exception.syndrome; + env->cp15.esr_el[new_el] = syndrome; break; +#ifndef CONFIG_USER_ONLY + case EXCP_ARMV8_HLT: + if (env->aarch64) { + if (semihosting_enabled) { + mask = env->exception.syndrome & 0xffff; + if (mask == 0xf000 ) { + env->xregs[0] = do_arm_semihosting(env); + qemu_log_mask(CPU_LOG_INT, "...handled a semihosting call\n"); + return; + } + } else { + cpu_abort(cs, "Skipping semihosting call!\n"); + } + } + break; +#endif case EXCP_IRQ: case EXCP_VIRQ: addr += 0x80; diff --git a/target-arm/helper.c b/target-arm/helper.c index e4e4931..4491b05 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -12,6 +12,7 @@ #include <zlib.h> /* For crc32 */ #ifndef CONFIG_USER_ONLY + static inline int get_phys_addr(CPUARMState *env, target_ulong address, int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, diff --git a/target-arm/internals.h b/target-arm/internals.h index 2cc3017..ddbbc0f 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -50,6 +50,7 @@ static const char * const excnames[] = { [EXCP_IRQ] = "IRQ", [EXCP_FIQ] = "FIQ", [EXCP_BKPT] = "Breakpoint", + [EXCP_ARMV8_HLT] = "HLT", [EXCP_EXCEPTION_EXIT] = "QEMU v7M exception exit", [EXCP_KERNEL_TRAP] = "QEMU intercept of kernel commpage", [EXCP_STREX] = "QEMU intercept of STREX", @@ -260,6 +261,13 @@ static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_thumb) | (is_thumb ? 0 : ARM_EL_IL); } +static inline uint32_t syn_aa64_hlt(uint32_t imm16) +{ + // architecturally, the syndrome is uncategorized. We add the imm16 field so + // that it can be accessed later for semihosting + return (EC_UNCATEGORIZED << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); +} + static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2, int crn, int crm, int rt, int isread) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 0b192a1..14a501c 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1544,7 +1544,7 @@ static void disas_exc(DisasContext *s, uint32_t insn) break; } /* HLT */ - unsupported_encoding(s, insn); + gen_exception_insn(s, 0, EXCP_ARMV8_HLT, syn_aa64_hlt(imm16)); break; case 5: if (op2_ll < 1 || op2_ll > 3) { -- Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project