summaryrefslogtreecommitdiffstats
path: root/linux-user/arm/cpu_loop.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user/arm/cpu_loop.c')
-rw-r--r--linux-user/arm/cpu_loop.c28
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;