diff options
Diffstat (limited to 'linux-user/mips/cpu_loop.c')
-rw-r--r-- | linux-user/mips/cpu_loop.c | 166 |
1 files changed, 33 insertions, 133 deletions
diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 32f9fc1c1c..9bb12a07ba 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -40,36 +40,32 @@ enum { BRK_DIVZERO = 7 }; -static int do_break(CPUMIPSState *env, target_siginfo_t *info, - unsigned int code) +static void do_tr_or_bp(CPUMIPSState *env, unsigned int code, bool trap) { - int ret = -1; + target_ulong pc = env->active_tc.PC; switch (code) { case BRK_OVERFLOW: + force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, pc); + break; case BRK_DIVZERO: - info->si_signo = TARGET_SIGFPE; - info->si_errno = 0; - info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; - queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); - ret = 0; + force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, pc); break; default: - info->si_signo = TARGET_SIGTRAP; - info->si_errno = 0; - queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); - ret = 0; + if (trap) { + force_sig(TARGET_SIGTRAP); + } else { + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, pc); + } break; } - - return ret; } void cpu_loop(CPUMIPSState *env) { CPUState *cs = env_cpu(env); - target_siginfo_t info; - int trapnr; + int trapnr, si_code; + unsigned int code; abi_long ret; # ifdef TARGET_ABI_MIPSO32 unsigned int syscall_num; @@ -160,149 +156,53 @@ done_syscall: break; case EXCP_CpU: case EXCP_RI: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + case EXCP_DSPDIS: + force_sig(TARGET_SIGILL); break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; case EXCP_DEBUG: - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_DSPDIS: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, + env->active_tc.PC); break; case EXCP_FPE: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_FLTUNK; + si_code = TARGET_FPE_FLTUNK; if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) { - info.si_code = TARGET_FPE_FLTINV; + si_code = TARGET_FPE_FLTINV; } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_DIV0) { - info.si_code = TARGET_FPE_FLTDIV; + si_code = TARGET_FPE_FLTDIV; } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_OVERFLOW) { - info.si_code = TARGET_FPE_FLTOVF; + si_code = TARGET_FPE_FLTOVF; } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_UNDERFLOW) { - info.si_code = TARGET_FPE_FLTUND; + si_code = TARGET_FPE_FLTUND; } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INEXACT) { - info.si_code = TARGET_FPE_FLTRES; + si_code = TARGET_FPE_FLTRES; } - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig_fault(TARGET_SIGFPE, si_code, env->active_tc.PC); break; + /* The code below was inspired by the MIPS Linux kernel trap * handling code in arch/mips/kernel/traps.c. */ case EXCP_BREAK: - { - abi_ulong trap_instr; - unsigned int code; - - if (env->hflags & MIPS_HFLAG_M16) { - if (env->insn_flags & ASE_MICROMIPS) { - /* microMIPS mode */ - ret = get_user_u16(trap_instr, env->active_tc.PC); - if (ret != 0) { - goto error; - } - - if ((trap_instr >> 10) == 0x11) { - /* 16-bit instruction */ - code = trap_instr & 0xf; - } else { - /* 32-bit instruction */ - abi_ulong instr_lo; - - ret = get_user_u16(instr_lo, - env->active_tc.PC + 2); - if (ret != 0) { - goto error; - } - trap_instr = (trap_instr << 16) | instr_lo; - code = ((trap_instr >> 6) & ((1 << 20) - 1)); - /* Unfortunately, microMIPS also suffers from - the old assembler bug... */ - if (code >= (1 << 10)) { - code >>= 10; - } - } - } else { - /* MIPS16e mode */ - ret = get_user_u16(trap_instr, env->active_tc.PC); - if (ret != 0) { - goto error; - } - code = (trap_instr >> 6) & 0x3f; - } - } else { - ret = get_user_u32(trap_instr, env->active_tc.PC); - if (ret != 0) { - goto error; - } - - /* As described in the original Linux kernel code, the - * below checks on 'code' are to work around an old - * assembly bug. - */ - code = ((trap_instr >> 6) & ((1 << 20) - 1)); - if (code >= (1 << 10)) { - code >>= 10; - } - } - - if (do_break(env, &info, code) != 0) { - goto error; - } + /* + * As described in the original Linux kernel code, the below + * checks on 'code' are to work around an old assembly bug. + */ + code = env->error_code; + if (code >= (1 << 10)) { + code >>= 10; } + do_tr_or_bp(env, code, false); break; case EXCP_TRAP: - { - abi_ulong trap_instr; - unsigned int code = 0; - - if (env->hflags & MIPS_HFLAG_M16) { - /* microMIPS mode */ - abi_ulong instr[2]; - - ret = get_user_u16(instr[0], env->active_tc.PC) || - get_user_u16(instr[1], env->active_tc.PC + 2); - - trap_instr = (instr[0] << 16) | instr[1]; - } else { - ret = get_user_u32(trap_instr, env->active_tc.PC); - } - - if (ret != 0) { - goto error; - } - - /* The immediate versions don't provide a code. */ - if (!(trap_instr & 0xFC000000)) { - if (env->hflags & MIPS_HFLAG_M16) { - /* microMIPS mode */ - code = ((trap_instr >> 12) & ((1 << 4) - 1)); - } else { - code = ((trap_instr >> 6) & ((1 << 10) - 1)); - } - } - - if (do_break(env, &info, code) != 0) { - goto error; - } - } + do_tr_or_bp(env, env->error_code, true); break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; default: -error: EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); abort(); } |