diff options
author | Richard Henderson <rth@twiddle.net> | 2010-04-07 15:42:26 -0700 |
---|---|---|
committer | Aurelien Jarno <aurelien@aurel32.net> | 2010-04-27 05:50:41 +0200 |
commit | 6910b8f66a9ad0c2c2052b4be884e11b76049718 (patch) | |
tree | 41ac594d78564a239e81f0b3b4b7a00a49525ec0 /linux-user/main.c | |
parent | 8aa3fa2038d9d0a7d69acdac505d990acc5eafc8 (diff) |
target-alpha: Fix load-locked/store-conditional.
Use an exception plus start_exclusive to implement the compare-and-swap.
This follows the example set by the MIPS and PPC ports.
Signed-off-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
Diffstat (limited to 'linux-user/main.c')
-rw-r--r-- | linux-user/main.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/linux-user/main.c b/linux-user/main.c index 5680d8e0c..18b52c00a 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2349,6 +2349,51 @@ void cpu_loop(CPUM68KState *env) #endif /* TARGET_M68K */ #ifdef TARGET_ALPHA +static void do_store_exclusive(CPUAlphaState *env, int reg, int quad) +{ + target_ulong addr, val, tmp; + target_siginfo_t info; + int ret = 0; + + addr = env->lock_addr; + tmp = env->lock_st_addr; + env->lock_addr = -1; + env->lock_st_addr = 0; + + start_exclusive(); + mmap_lock(); + + if (addr == tmp) { + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto do_sigsegv; + } + + if (val == env->lock_value) { + tmp = env->ir[reg]; + if (quad ? put_user_u64(tmp, addr) : put_user_u32(tmp, addr)) { + goto do_sigsegv; + } + ret = 1; + } + } + env->ir[reg] = ret; + env->pc += 4; + + mmap_unlock(); + end_exclusive(); + return; + + do_sigsegv: + mmap_unlock(); + end_exclusive(); + + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = addr; + queue_signal(env, TARGET_SIGSEGV, &info); +} + void cpu_loop (CPUState *env) { int trapnr; @@ -2373,6 +2418,7 @@ void cpu_loop (CPUState *env) exit(1); break; case EXCP_ARITH: + env->lock_addr = -1; info.si_signo = TARGET_SIGFPE; info.si_errno = 0; info.si_code = TARGET_FPE_FLTINV; @@ -2384,6 +2430,7 @@ void cpu_loop (CPUState *env) exit(1); break; case EXCP_DFAULT: + env->lock_addr = -1; info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = 0; /* ??? SEGV_MAPERR vs SEGV_ACCERR. */ @@ -2407,6 +2454,7 @@ void cpu_loop (CPUState *env) exit(1); break; case EXCP_UNALIGN: + env->lock_addr = -1; info.si_signo = TARGET_SIGBUS; info.si_errno = 0; info.si_code = TARGET_BUS_ADRALN; @@ -2415,6 +2463,7 @@ void cpu_loop (CPUState *env) break; case EXCP_OPCDEC: do_sigill: + env->lock_addr = -1; info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_ILLOPC; @@ -2425,6 +2474,7 @@ void cpu_loop (CPUState *env) /* No-op. Linux simply re-enables the FPU. */ break; case EXCP_CALL_PAL ... (EXCP_CALL_PALP - 1): + env->lock_addr = -1; switch ((trapnr >> 6) | 0x80) { case 0x80: /* BPT */ @@ -2514,11 +2564,16 @@ void cpu_loop (CPUState *env) case EXCP_DEBUG: info.si_signo = gdb_handlesig (env, TARGET_SIGTRAP); if (info.si_signo) { + env->lock_addr = -1; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; queue_signal(env, info.si_signo, &info); } break; + case EXCP_STL_C: + case EXCP_STQ_C: + do_store_exclusive(env, env->error_code, trapnr - EXCP_STL_C); + break; default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(env, stderr, fprintf, 0); |