summaryrefslogtreecommitdiff
path: root/linux-user
diff options
context:
space:
mode:
authorRichard Henderson <rth@twiddle.net>2010-04-07 15:42:26 -0700
committerAurelien Jarno <aurelien@aurel32.net>2010-04-27 05:50:41 +0200
commit6910b8f66a9ad0c2c2052b4be884e11b76049718 (patch)
tree41ac594d78564a239e81f0b3b4b7a00a49525ec0 /linux-user
parent8aa3fa2038d9d0a7d69acdac505d990acc5eafc8 (diff)
downloadqemu-6910b8f66a9ad0c2c2052b4be884e11b76049718.tar.gz
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')
-rw-r--r--linux-user/main.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index 5680d8e0cd..18b52c00a4 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);