summaryrefslogtreecommitdiff
path: root/linux-user
diff options
context:
space:
mode:
authorNathan Froyd <froydnj@codesourcery.com>2009-08-03 08:43:27 -0700
committermalc <av1474@comtv.ru>2009-08-03 20:33:41 +0400
commit56f066bb17cde3044f8db00c2245301b5a466e3f (patch)
treec0f366a3fd25bef667710a7ae068b2ce98a0f87b /linux-user
parent4425265beb8503b82dc3de0388b504e15825d06e (diff)
downloadqemu-56f066bb17cde3044f8db00c2245301b5a466e3f.tar.gz
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>
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/main.c68
1 files changed, 68 insertions, 0 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index 20a25769f9..bc903dec0a 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -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;