linux-user: handle POWERPC_EXCP_STCX

We handle conditional stores as an exception so we can ensure that no
other thread is changing memory out from underneath us.

Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
Signed-off-by: malc <av1474@comtv.ru>
This commit is contained in:
Nathan Froyd 2009-08-03 08:43:27 -07:00 committed by malc
parent 4425265beb
commit 56f066bb17

View File

@ -1060,6 +1060,63 @@ do { \
log_cpu_state(env, 0); \
} while (0)
static int do_store_exclusive(CPUPPCState *env)
{
target_ulong addr;
target_ulong page_addr;
target_ulong val;
int flags;
int segv = 0;
addr = env->reserve_ea;
page_addr = addr & TARGET_PAGE_MASK;
start_exclusive();
mmap_lock();
flags = page_get_flags(page_addr);
if ((flags & PAGE_READ) == 0) {
segv = 1;
} else {
int reg = env->reserve_info & 0x1f;
int size = (env->reserve_info >> 5) & 0xf;
int stored = 0;
if (addr == env->reserve_addr) {
switch (size) {
case 1: segv = get_user_u8(val, addr); break;
case 2: segv = get_user_u16(val, addr); break;
case 4: segv = get_user_u32(val, addr); break;
#if defined(TARGET_PPC64)
case 8: segv = get_user_u64(val, addr); break;
#endif
default: abort();
}
if (!segv && val == env->reserve_val) {
val = env->gpr[reg];
switch (size) {
case 1: segv = put_user_u8(val, addr); break;
case 2: segv = put_user_u16(val, addr); break;
case 4: segv = put_user_u32(val, addr); break;
#if defined(TARGET_PPC64)
case 8: segv = put_user_u64(val, addr); break;
#endif
default: abort();
}
if (!segv) {
stored = 1;
}
}
}
env->crf[0] = (stored << 1) | xer_so;
env->reserve_addr = (target_ulong)-1;
}
if (!segv) {
env->nip += 4;
}
mmap_unlock();
end_exclusive();
return segv;
}
void cpu_loop(CPUPPCState *env)
{
target_siginfo_t info;
@ -1067,7 +1124,9 @@ void cpu_loop(CPUPPCState *env)
uint32_t ret;
for(;;) {
cpu_exec_start(env);
trapnr = cpu_ppc_exec(env);
cpu_exec_end(env);
switch(trapnr) {
case POWERPC_EXCP_NONE:
/* Just go on */
@ -1447,6 +1506,15 @@ void cpu_loop(CPUPPCState *env)
printf("syscall returned 0x%08x (%d)\n", ret, ret);
#endif
break;
case POWERPC_EXCP_STCX:
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->nip;
queue_signal(env, info.si_signo, &info);
}
break;
case EXCP_DEBUG:
{
int sig;