diff options
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/aarch64/syscall.h | 1 | ||||
-rw-r--r-- | linux-user/aarch64/target_cpu.h | 5 | ||||
-rw-r--r-- | linux-user/arm/target_cpu.h | 2 | ||||
-rw-r--r-- | linux-user/elfload.c | 3 | ||||
-rw-r--r-- | linux-user/flatload.c | 3 | ||||
-rw-r--r-- | linux-user/linuxload.c | 4 | ||||
-rw-r--r-- | linux-user/main.c | 154 | ||||
-rw-r--r-- | linux-user/qemu.h | 6 | ||||
-rw-r--r-- | linux-user/signal.c | 24 | ||||
-rw-r--r-- | linux-user/syscall.c | 16 | ||||
-rw-r--r-- | linux-user/syscall_defs.h | 1 |
11 files changed, 184 insertions, 35 deletions
diff --git a/linux-user/aarch64/syscall.h b/linux-user/aarch64/syscall.h index aef419efeb..18f44a8a40 100644 --- a/linux-user/aarch64/syscall.h +++ b/linux-user/aarch64/syscall.h @@ -7,3 +7,4 @@ struct target_pt_regs { #define UNAME_MACHINE "aarch64" #define UNAME_MINIMUM_RELEASE "3.8.0" +#define TARGET_CLONE_BACKWARDS diff --git a/linux-user/aarch64/target_cpu.h b/linux-user/aarch64/target_cpu.h index 6f5539b50f..21560ef832 100644 --- a/linux-user/aarch64/target_cpu.h +++ b/linux-user/aarch64/target_cpu.h @@ -29,7 +29,10 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp) static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) { - env->sr.tpidr_el0 = newtls; + /* Note that AArch64 Linux keeps the TLS pointer in TPIDR; this is + * different from AArch32 Linux, which uses TPIDRRO. + */ + env->cp15.tpidr_el0 = newtls; } #endif diff --git a/linux-user/arm/target_cpu.h b/linux-user/arm/target_cpu.h index ed323c079d..39d65b692b 100644 --- a/linux-user/arm/target_cpu.h +++ b/linux-user/arm/target_cpu.h @@ -29,7 +29,7 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp) static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) { - env->cp15.c13_tls2 = newtls; + env->cp15.tpidrro_el0 = newtls; } #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8dd424dadd..5902f162b4 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1998,8 +1998,7 @@ give_up: free(syms); } -int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, - struct image_info * info) +int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) { struct image_info interp_info; struct elfhdr elf_ex; diff --git a/linux-user/flatload.c b/linux-user/flatload.c index ceb89bb6ea..566a7a87a3 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -704,8 +704,7 @@ static int load_flat_shared_library(int id, struct lib_info *libs) #endif /* CONFIG_BINFMT_SHARED_FLAT */ -int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, - struct image_info * info) +int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) { struct lib_info libinfo[MAX_SHARED_LIBS]; abi_ulong p = bprm->p; diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index a1fe5ed9ae..f2997c2f4b 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -154,13 +154,13 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, && bprm->buf[1] == 'E' && bprm->buf[2] == 'L' && bprm->buf[3] == 'F') { - retval = load_elf_binary(bprm, regs, infop); + retval = load_elf_binary(bprm, infop); #if defined(TARGET_HAS_BFLT) } else if (bprm->buf[0] == 'b' && bprm->buf[1] == 'F' && bprm->buf[2] == 'L' && bprm->buf[3] == 'T') { - retval = load_flt_binary(bprm,regs,infop); + retval = load_flt_binary(bprm, infop); #endif } else { return -ENOEXEC; diff --git a/linux-user/main.c b/linux-user/main.c index 54f71fe8f6..cabc9e1a0e 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -566,7 +566,7 @@ do_kernel_trap(CPUARMState *env) end_exclusive(); break; case 0xffff0fe0: /* __kernel_get_tls */ - env->regs[0] = env->cp15.c13_tls2; + env->regs[0] = env->cp15.tpidrro_el0; break; case 0xffff0f60: /* __kernel_cmpxchg64 */ arm_kernel_cmpxchg64_helper(env); @@ -585,20 +585,25 @@ do_kernel_trap(CPUARMState *env) return 0; } -#endif +/* Store exclusive handling for AArch32 */ static int do_strex(CPUARMState *env) { - uint32_t val; + uint64_t val; int size; int rc = 1; int segv = 0; uint32_t addr; start_exclusive(); - addr = env->exclusive_addr; - if (addr != env->exclusive_test) { + if (env->exclusive_addr != env->exclusive_test) { goto fail; } + /* We know we're always AArch32 so the address is in uint32_t range + * unless it was the -1 exclusive-monitor-lost value (which won't + * match exclusive_test above). + */ + assert(extract64(env->exclusive_addr, 32, 32) == 0); + addr = env->exclusive_addr; size = env->exclusive_info & 0xf; switch (size) { case 0: @@ -618,19 +623,19 @@ static int do_strex(CPUARMState *env) env->cp15.c6_data = addr; goto done; } - if (val != env->exclusive_val) { - goto fail; - } if (size == 3) { - segv = get_user_u32(val, addr + 4); + uint32_t valhi; + segv = get_user_u32(valhi, addr + 4); if (segv) { env->cp15.c6_data = addr + 4; goto done; } - if (val != env->exclusive_high) { - goto fail; - } + val = deposit64(val, 32, 32, valhi); } + if (val != env->exclusive_val) { + goto fail; + } + val = env->regs[(env->exclusive_info >> 8) & 0xf]; switch (size) { case 0: @@ -665,7 +670,6 @@ done: return segv; } -#ifdef TARGET_ABI32 void cpu_loop(CPUARMState *env) { CPUState *cs = CPU(arm_env_get_cpu(env)); @@ -880,6 +884,122 @@ void cpu_loop(CPUARMState *env) #else +/* + * Handle AArch64 store-release exclusive + * + * rs = gets the status result of store exclusive + * rt = is the register that is stored + * rt2 = is the second register store (in STP) + * + */ +static int do_strex_a64(CPUARMState *env) +{ + uint64_t val; + int size; + bool is_pair; + int rc = 1; + int segv = 0; + uint64_t addr; + int rs, rt, rt2; + + start_exclusive(); + /* size | is_pair << 2 | (rs << 4) | (rt << 9) | (rt2 << 14)); */ + size = extract32(env->exclusive_info, 0, 2); + is_pair = extract32(env->exclusive_info, 2, 1); + rs = extract32(env->exclusive_info, 4, 5); + rt = extract32(env->exclusive_info, 9, 5); + rt2 = extract32(env->exclusive_info, 14, 5); + + addr = env->exclusive_addr; + + if (addr != env->exclusive_test) { + goto finish; + } + + switch (size) { + case 0: + segv = get_user_u8(val, addr); + break; + case 1: + segv = get_user_u16(val, addr); + break; + case 2: + segv = get_user_u32(val, addr); + break; + case 3: + segv = get_user_u64(val, addr); + break; + default: + abort(); + } + if (segv) { + env->cp15.c6_data = addr; + goto error; + } + if (val != env->exclusive_val) { + goto finish; + } + if (is_pair) { + if (size == 2) { + segv = get_user_u32(val, addr + 4); + } else { + segv = get_user_u64(val, addr + 8); + } + if (segv) { + env->cp15.c6_data = addr + (size == 2 ? 4 : 8); + goto error; + } + if (val != env->exclusive_high) { + goto finish; + } + } + val = env->xregs[rt]; + switch (size) { + case 0: + segv = put_user_u8(val, addr); + break; + case 1: + segv = put_user_u16(val, addr); + break; + case 2: + segv = put_user_u32(val, addr); + break; + case 3: + segv = put_user_u64(val, addr); + break; + } + if (segv) { + goto error; + } + if (is_pair) { + val = env->xregs[rt2]; + if (size == 2) { + segv = put_user_u32(val, addr + 4); + } else { + segv = put_user_u64(val, addr + 8); + } + if (segv) { + env->cp15.c6_data = addr + (size == 2 ? 4 : 8); + goto error; + } + } + rc = 0; +finish: + env->pc += 4; + /* rs == 31 encodes a write to the ZR, thus throwing away + * the status return. This is rather silly but valid. + */ + if (rs < 31) { + env->xregs[rs] = rc; + } +error: + /* instruction faulted, PC does not advance */ + /* either way a strex releases any exclusive lock we have */ + env->exclusive_addr = -1; + end_exclusive(); + return segv; +} + /* AArch64 main loop */ void cpu_loop(CPUARMState *env) { @@ -939,7 +1059,7 @@ void cpu_loop(CPUARMState *env) } break; case EXCP_STREX: - if (do_strex(env)) { + if (do_strex_a64(env)) { addr = env->cp15.c6_data; goto do_segv; } @@ -951,6 +1071,12 @@ void cpu_loop(CPUARMState *env) abort(); } process_pending_signals(env); + /* Exception return on AArch64 always clears the exclusive monitor, + * so any return to running guest code implies this. + * A strex (successful or otherwise) also clears the monitor, so + * we don't need to specialcase EXCP_STREX. + */ + env->exclusive_addr = -1; } } #endif /* ndef TARGET_ABI32 */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index e2717e0775..c2f74f33d6 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -178,10 +178,8 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, struct target_pt_regs * regs, struct image_info *infop, struct linux_binprm *); -int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, - struct image_info * info); -int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, - struct image_info * info); +int load_elf_binary(struct linux_binprm *bprm, struct image_info *info); +int load_flt_binary(struct linux_binprm *bprm, struct image_info *info); abi_long memcpy_to_target(abi_ulong dest, const void *src, unsigned long len); diff --git a/linux-user/signal.c b/linux-user/signal.c index 7751c47ef1..01d7c393df 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1171,7 +1171,7 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf, } __put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); __put_user(env->pc, &sf->uc.tuc_mcontext.pc); - __put_user(env->pstate, &sf->uc.tuc_mcontext.pstate); + __put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate); __put_user(/*current->thread.fault_address*/ 0, &sf->uc.tuc_mcontext.fault_address); @@ -1189,8 +1189,8 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf, __put_user(env->vfp.regs[i * 2 + 1], &aux->fpsimd.vregs[i * 2 + 1]); #endif } - __put_user(/*env->fpsr*/0, &aux->fpsimd.fpsr); - __put_user(/*env->fpcr*/0, &aux->fpsimd.fpcr); + __put_user(vfp_get_fpsr(env), &aux->fpsimd.fpsr); + __put_user(vfp_get_fpcr(env), &aux->fpsimd.fpcr); __put_user(TARGET_FPSIMD_MAGIC, &aux->fpsimd.head.magic); __put_user(sizeof(struct target_fpsimd_context), &aux->fpsimd.head.size); @@ -1209,7 +1209,8 @@ static int target_restore_sigframe(CPUARMState *env, int i; struct target_aux_context *aux = (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved; - uint32_t magic, size; + uint32_t magic, size, fpsr, fpcr; + uint64_t pstate; target_to_host_sigset(&set, &sf->uc.tuc_sigmask); sigprocmask(SIG_SETMASK, &set, NULL); @@ -1220,7 +1221,8 @@ static int target_restore_sigframe(CPUARMState *env, __get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); __get_user(env->pc, &sf->uc.tuc_mcontext.pc); - __get_user(env->pstate, &sf->uc.tuc_mcontext.pstate); + __get_user(pstate, &sf->uc.tuc_mcontext.pstate); + pstate_write(env, pstate); __get_user(magic, &aux->fpsimd.head.magic); __get_user(size, &aux->fpsimd.head.size); @@ -1233,6 +1235,10 @@ static int target_restore_sigframe(CPUARMState *env, for (i = 0; i < 32 * 2; i++) { __get_user(env->vfp.regs[i], &aux->fpsimd.vregs[i]); } + __get_user(fpsr, &aux->fpsimd.fpsr); + vfp_set_fpsr(env, fpsr); + __get_user(fpcr, &aux->fpsimd.fpcr); + vfp_set_fpcr(env, fpcr); return 0; } @@ -2537,9 +2543,9 @@ void sparc64_set_context(CPUSPARCState *env) abi_ulong *src, *dst; src = ucp->tuc_sigmask.sig; dst = target_set.sig; - for (i = 0; i < sizeof(target_sigset_t) / sizeof(abi_ulong); - i++, dst++, src++) + for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { err |= __get_user(*dst, src); + } if (err) goto do_sigsegv; } @@ -2642,9 +2648,9 @@ void sparc64_get_context(CPUSPARCState *env) abi_ulong *src, *dst; src = target_set.sig; dst = ucp->tuc_sigmask.sig; - for (i = 0; i < sizeof(target_sigset_t) / sizeof(abi_ulong); - i++, dst++, src++) + for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { err |= __put_user(*src, dst); + } if (err) goto do_sigsegv; } diff --git a/linux-user/syscall.c b/linux-user/syscall.c index efd1453987..0ac05b85f2 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2245,6 +2245,22 @@ static abi_long do_socketcall(int num, abi_ulong vptr) ret = do_accept4(sockfd, target_addr, target_addrlen, 0); } break; + case SOCKOP_accept4: + { + abi_ulong sockfd; + abi_ulong target_addr, target_addrlen; + abi_ulong flags; + + if (get_user_ual(sockfd, vptr) + || get_user_ual(target_addr, vptr + n) + || get_user_ual(target_addrlen, vptr + 2 * n) + || get_user_ual(flags, vptr + 3 * n)) { + return -TARGET_EFAULT; + } + + ret = do_accept4(sockfd, target_addr, target_addrlen, flags); + } + break; case SOCKOP_getsockname: { abi_ulong sockfd; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index cf08db5a23..ae30476217 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -27,6 +27,7 @@ #define SOCKOP_getsockopt 15 #define SOCKOP_sendmsg 16 #define SOCKOP_recvmsg 17 +#define SOCKOP_accept4 18 #define IPCOP_semop 1 #define IPCOP_semget 2 |