diff options
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/arm/cpu_loop.c | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 13629ee1f6..3d272b56ef 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -205,6 +205,24 @@ do_kernel_trap(CPUARMState *env) return 0; } +static bool insn_is_linux_bkpt(uint32_t opcode, bool is_thumb) +{ + /* + * Return true if this insn is one of the three magic UDF insns + * which the kernel treats as breakpoint insns. + */ + if (!is_thumb) { + return (opcode & 0x0fffffff) == 0x07f001f0; + } else { + /* + * Note that we get the two halves of the 32-bit T32 insn + * in the opposite order to the value the kernel uses in + * its undef_hook struct. + */ + return ((opcode & 0xffff) == 0xde01) || (opcode == 0xa000f7f0); + } +} + void cpu_loop(CPUARMState *env) { CPUState *cs = env_cpu(env); @@ -234,6 +252,16 @@ void cpu_loop(CPUARMState *env) /* FIXME - what to do if get_user() fails? */ get_user_code_u32(opcode, env->regs[15], env); + /* + * The Linux kernel treats some UDF patterns specially + * to use as breakpoints (instead of the architectural + * bkpt insn). These should trigger a SIGTRAP rather + * than SIGILL. + */ + if (insn_is_linux_bkpt(opcode, env->thumb)) { + goto excp_debug; + } + rc = EmulateAll(opcode, &ts->fpa, env); if (rc == 0) { /* illegal instruction */ info.si_signo = TARGET_SIGILL; |