aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Brook <paul@codesourcery.com>2009-07-09 17:45:17 +0100
committerPaul Brook <paul@codesourcery.com>2009-07-09 17:45:17 +0100
commit590bc601d800d16a77676926898019f7285bd615 (patch)
tree85cfd216edb9e8a3068cb4848f238e1fe4fdef58
parentff867ddcbd55af7becd2328a454e0158018fb50e (diff)
downloadqemu-590bc601d800d16a77676926898019f7285bd615.zip
qemu-590bc601d800d16a77676926898019f7285bd615.tar.gz
qemu-590bc601d800d16a77676926898019f7285bd615.tar.bz2
MIPS atomic instructions
Implement MIPS ll/sc instructions using atomic compare+exchange. Signed-off-by: Paul Brook <paul@codesourcery.com>
-rw-r--r--linux-user/main.c60
-rw-r--r--target-mips/cpu.h5
-rw-r--r--target-mips/translate.c83
3 files changed, 124 insertions, 24 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index 7348447..30290a5 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 6ebb82b..bb9a49b 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 d316b9d..cf467f8 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