diff --git a/linux-user/main.c b/linux-user/main.c index 734844746b..30290a5837 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1826,6 +1826,55 @@ static const uint8_t mips_syscall_args[] = { #undef MIPS_SYS +static int do_store_exclusive(CPUMIPSState *env) +{ + target_ulong addr; + target_ulong page_addr; + target_ulong val; + int flags; + int segv = 0; + int reg; + int d; + + addr = env->CP0_LLAddr; + page_addr = addr & TARGET_PAGE_MASK; + start_exclusive(); + mmap_lock(); + flags = page_get_flags(page_addr); + if ((flags & PAGE_READ) == 0) { + segv = 1; + } else { + reg = env->llreg & 0x1f; + d = (env->llreg & 0x20) != 0; + if (d) { + segv = get_user_s64(val, addr); + } else { + segv = get_user_s32(val, addr); + } + if (!segv) { + if (val != env->llval) { + env->active_tc.gpr[reg] = 0; + } else { + if (d) { + segv = put_user_u64(env->llnewval, addr); + } else { + segv = put_user_u32(env->llnewval, addr); + } + if (!segv) { + env->active_tc.gpr[reg] = 1; + } + } + } + } + env->CP0_LLAddr = -1; + if (!segv) { + env->active_tc.PC += 4; + } + mmap_unlock(); + end_exclusive(); + return segv; +} + void cpu_loop(CPUMIPSState *env) { target_siginfo_t info; @@ -1833,7 +1882,9 @@ void cpu_loop(CPUMIPSState *env) unsigned int syscall_num; for(;;) { + cpu_exec_start(env); trapnr = cpu_mips_exec(env); + cpu_exec_end(env); switch(trapnr) { case EXCP_SYSCALL: syscall_num = env->active_tc.gpr[2] - 4000; @@ -1910,6 +1961,15 @@ void cpu_loop(CPUMIPSState *env) } } break; + case EXCP_SC: + if (do_store_exclusive(env)) { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->active_tc.PC; + queue_signal(env, info.si_signo, &info); + } + break; default: // error: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 6ebb82bccb..bb9a49b985 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -375,6 +375,9 @@ struct CPUMIPSState { int32_t CP0_Config7; /* XXX: Maybe make LLAddr per-TC? */ target_ulong CP0_LLAddr; + target_ulong llval; + target_ulong llnewval; + target_ulong llreg; target_ulong CP0_WatchLo[8]; int32_t CP0_WatchHi[8]; target_ulong CP0_XContext; @@ -559,6 +562,8 @@ enum { EXCP_LAST = EXCP_CACHE, }; +/* Dummy exception for conditional stores. */ +#define EXCP_SC 0x100 int cpu_mips_exec(CPUMIPSState *s); CPUMIPSState *cpu_mips_init(const char *cpu_model); diff --git a/target-mips/translate.c b/target-mips/translate.c index d316b9d1e4..cf467f897b 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -919,6 +919,7 @@ static inline void op_ldst_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \ tcg_gen_mov_tl(t0, arg1); \ tcg_gen_qemu_##fname(ret, arg1, ctx->mem_idx); \ tcg_gen_st_tl(t0, cpu_env, offsetof(CPUState, CP0_LLAddr)); \ + tcg_gen_st_tl(ret, cpu_env, offsetof(CPUState, llval)); \ tcg_temp_free(t0); \ } OP_LD_ATOMIC(ll,ld32s); @@ -927,32 +928,66 @@ OP_LD_ATOMIC(lld,ld64); #endif #undef OP_LD_ATOMIC -#define OP_ST_ATOMIC(insn,fname,almask) \ -static inline void op_ldst_##insn(TCGv ret, TCGv arg1, TCGv arg2, DisasContext *ctx) \ -{ \ - TCGv t0 = tcg_temp_new(); \ - int l1 = gen_new_label(); \ - int l2 = gen_new_label(); \ - int l3 = gen_new_label(); \ - \ - tcg_gen_andi_tl(t0, arg2, almask); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); \ - tcg_gen_st_tl(arg2, cpu_env, offsetof(CPUState, CP0_BadVAddr)); \ - generate_exception(ctx, EXCP_AdES); \ - gen_set_label(l1); \ - tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, CP0_LLAddr)); \ - tcg_gen_brcond_tl(TCG_COND_NE, arg2, t0, l2); \ - tcg_temp_free(t0); \ - tcg_gen_qemu_##fname(arg1, arg2, ctx->mem_idx); \ - tcg_gen_movi_tl(ret, 1); \ - tcg_gen_br(l3); \ - gen_set_label(l2); \ - tcg_gen_movi_tl(ret, 0); \ - gen_set_label(l3); \ +#ifdef CONFIG_USER_ONLY +#define OP_ST_ATOMIC(insn,fname,ldname,almask) \ +static inline void op_ldst_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \ +{ \ + TCGv t0 = tcg_temp_new(); \ + int l1 = gen_new_label(); \ + int l2 = gen_new_label(); \ + \ + tcg_gen_andi_tl(t0, arg2, almask); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); \ + tcg_gen_st_tl(arg2, cpu_env, offsetof(CPUState, CP0_BadVAddr)); \ + generate_exception(ctx, EXCP_AdES); \ + gen_set_label(l1); \ + tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, CP0_LLAddr)); \ + tcg_gen_brcond_tl(TCG_COND_NE, arg2, t0, l2); \ + tcg_gen_movi_tl(t0, rt | ((almask << 3) & 0x20)); \ + tcg_gen_st_tl(t0, cpu_env, offsetof(CPUState, llreg)); \ + tcg_gen_st_tl(arg1, cpu_env, offsetof(CPUState, llnewval)); \ + gen_helper_0i(raise_exception, EXCP_SC); \ + gen_set_label(l2); \ + tcg_gen_movi_tl(t0, 0); \ + gen_store_gpr(t0, rt); \ + tcg_temp_free(t0); \ } -OP_ST_ATOMIC(sc,st32,0x3); +#else +#define OP_ST_ATOMIC(insn,fname,ldname,almask) \ +static inline void op_ldst_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \ +{ \ + TCGv t0 = tcg_temp_new(); \ + TCGv t1 = tcg_temp_new(); \ + int l1 = gen_new_label(); \ + int l2 = gen_new_label(); \ + int l3 = gen_new_label(); \ + \ + tcg_gen_andi_tl(t0, arg2, almask); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); \ + tcg_gen_st_tl(arg2, cpu_env, offsetof(CPUState, CP0_BadVAddr)); \ + generate_exception(ctx, EXCP_AdES); \ + gen_set_label(l1); \ + tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, CP0_LLAddr)); \ + tcg_gen_brcond_tl(TCG_COND_NE, arg2, t0, l2); \ + tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, llval)); \ + tcg_gen_qemu_##ldname(t1, arg2, ctx->mem_idx); \ + tcg_gen_brcond_tl(TCG_COND_NE, t0, t1, l2); \ + tcg_temp_free(t1); \ + tcg_gen_qemu_##fname(arg1, arg2, ctx->mem_idx); \ + tcg_gen_movi_tl(t0, 1); \ + gen_store_gpr(t0, rt); \ + tcg_gen_br(l3); \ + gen_set_label(l2); \ + tcg_gen_movi_tl(t0, 0); \ + gen_store_gpr(t0, rt); \ + gen_set_label(l3); \ + tcg_temp_free(t0); \ +} +#endif + +OP_ST_ATOMIC(sc,st32,ld32s,0x3); #if defined(TARGET_MIPS64) -OP_ST_ATOMIC(scd,st64,0x7); +OP_ST_ATOMIC(scd,st64,ld64,0x7); #endif #undef OP_ST_ATOMIC