diff options
Diffstat (limited to 'linux-user/main.c')
-rw-r--r-- | linux-user/main.c | 60 |
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", |