summary refs log tree commit diff stats
path: root/linux-user/main.c
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 /linux-user/main.c
parentff867ddcbd55af7becd2328a454e0158018fb50e (diff)
downloadfocaccia-qemu-590bc601d800d16a77676926898019f7285bd615.tar.gz
focaccia-qemu-590bc601d800d16a77676926898019f7285bd615.zip
MIPS atomic instructions
Implement MIPS ll/sc instructions using atomic compare+exchange.

Signed-off-by: Paul Brook <paul@codesourcery.com>
Diffstat (limited to 'linux-user/main.c')
-rw-r--r--linux-user/main.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index 734844746b..30290a5837 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",