diff mbox

[PULL,13/13] target-arm: Fix CPU breakpoint handling

Message ID 1445003887-14475-14-git-send-email-peter.maydell@linaro.org
State Accepted
Commit 5d98bf8f38c17a348ab6e8af196088cd4953acd0
Headers show

Commit Message

Peter Maydell Oct. 16, 2015, 1:58 p.m. UTC
From: Sergey Fedorov <serge.fdrv@gmail.com>

A QEMU breakpoint match is not definitely an architectural breakpoint
match. If an exception is generated unconditionally during translation,
it is hardly possible to ignore it in the debug exception handler.

Generate a call to a helper to check CPU breakpoints and raise an
exception only if any breakpoint matches architecturally.

Signed-off-by: Sergey Fedorov <serge.fdrv@gmail.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 target-arm/helper.h        |  2 ++
 target-arm/op_helper.c     | 29 ++++++++++++++++++-----------
 target-arm/translate-a64.c | 17 ++++++++++++-----
 target-arm/translate.c     | 19 ++++++++++++++-----
 4 files changed, 46 insertions(+), 21 deletions(-)

Comments

Peter Maydell Nov. 2, 2015, 11:09 a.m. UTC | #1
On 21 October 2015 at 19:15, Sergey Fedorov <serge.fdrv@gmail.com> wrote:
> On 16.10.2015 16:58, Peter Maydell wrote:

>> From: Sergey Fedorov <serge.fdrv@gmail.com>

>>

>> A QEMU breakpoint match is not definitely an architectural breakpoint

>> match. If an exception is generated unconditionally during translation,

>> it is hardly possible to ignore it in the debug exception handler.

>>

>> Generate a call to a helper to check CPU breakpoints and raise an

>> exception only if any breakpoint matches architecturally.

>>

>> Signed-off-by: Sergey Fedorov <serge.fdrv@gmail.com>

>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

>> ---


> It turns out that this change introduced an issue which can be

> illustrated by the following test:


> I think we could fix this problem by cleaning up DISAS_UPDATE usage in

> target-arm/translate.c and implementing PC update as in

> target-arm/translate-a64.c. I could prepare a patch for that.

>

> Another problem, I think, is that we should somehow restore the CPU

> state before raising an exception from check_breakpoints() helper. But

> so far I have no idea how to fix this...


Hi, Sergey -- how are you doing with the fix for this? It would
be good to get it in and tested soon, because hardfreeze is next
week.

I've also had a report that this patch broke gdbstub single-stepping,
which might be the same underlying cause.

thanks
-- PMM
diff mbox

Patch

diff --git a/target-arm/helper.h b/target-arm/helper.h
index 827b33d..c2a85c7 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -54,6 +54,8 @@  DEF_HELPER_1(yield, void, env)
 DEF_HELPER_1(pre_hvc, void, env)
 DEF_HELPER_2(pre_smc, void, env, i32)
 
+DEF_HELPER_1(check_breakpoints, void, env)
+
 DEF_HELPER_3(cpsr_write, void, env, i32, i32)
 DEF_HELPER_1(cpsr_read, i32, env)
 
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index 67b18c0..7929c71 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -867,6 +867,15 @@  static bool check_breakpoints(ARMCPU *cpu)
     return false;
 }
 
+void HELPER(check_breakpoints)(CPUARMState *env)
+{
+    ARMCPU *cpu = arm_env_get_cpu(env);
+
+    if (check_breakpoints(cpu)) {
+        HELPER(exception_internal(env, EXCP_DEBUG));
+    }
+}
+
 void arm_debug_excp_handler(CPUState *cs)
 {
     /* Called by core code when a watchpoint or breakpoint fires;
@@ -898,23 +907,21 @@  void arm_debug_excp_handler(CPUState *cs)
         }
     } else {
         uint64_t pc = is_a64(env) ? env->pc : env->regs[15];
+        bool same_el = (arm_debug_target_el(env) == arm_current_el(env));
 
         if (cpu_breakpoint_test(cs, pc, BP_GDB)) {
             return;
         }
 
-        if (check_breakpoints(cpu)) {
-            bool same_el = (arm_debug_target_el(env) == arm_current_el(env));
-            if (extended_addresses_enabled(env)) {
-                env->exception.fsr = (1 << 9) | 0x22;
-            } else {
-                env->exception.fsr = 0x2;
-            }
-            /* FAR is UNKNOWN, so doesn't need setting */
-            raise_exception(env, EXCP_PREFETCH_ABORT,
-                            syn_breakpoint(same_el),
-                            arm_debug_target_el(env));
+        if (extended_addresses_enabled(env)) {
+            env->exception.fsr = (1 << 9) | 0x22;
+        } else {
+            env->exception.fsr = 0x2;
         }
+        /* FAR is UNKNOWN, so doesn't need setting */
+        raise_exception(env, EXCP_PREFETCH_ABORT,
+                        syn_breakpoint(same_el),
+                        arm_debug_target_el(env));
     }
 }
 
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index a4580c0..19f9d8d 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -11090,11 +11090,18 @@  void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb)
             CPUBreakpoint *bp;
             QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
                 if (bp->pc == dc->pc) {
-                    gen_exception_internal_insn(dc, 0, EXCP_DEBUG);
-                    /* Advance PC so that clearing the breakpoint will
-                       invalidate this TB.  */
-                    dc->pc += 2;
-                    goto done_generating;
+                    if (bp->flags & BP_CPU) {
+                        gen_helper_check_breakpoints(cpu_env);
+                        /* End the TB early; it likely won't be executed */
+                        dc->is_jmp = DISAS_UPDATE;
+                    } else {
+                        gen_exception_internal_insn(dc, 0, EXCP_DEBUG);
+                        /* Advance PC so that clearing the breakpoint will
+                           invalidate this TB.  */
+                        dc->pc += 4;
+                        goto done_generating;
+                    }
+                    break;
                 }
             }
         }
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 1273000..9f1d740 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -11342,11 +11342,20 @@  void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb)
             CPUBreakpoint *bp;
             QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
                 if (bp->pc == dc->pc) {
-                    gen_exception_internal_insn(dc, 0, EXCP_DEBUG);
-                    /* Advance PC so that clearing the breakpoint will
-                       invalidate this TB.  */
-                    dc->pc += 2;
-                    goto done_generating;
+                    if (bp->flags & BP_CPU) {
+                        gen_helper_check_breakpoints(cpu_env);
+                        /* End the TB early; it's likely not going to be executed */
+                        dc->is_jmp = DISAS_UPDATE;
+                    } else {
+                        gen_exception_internal_insn(dc, 0, EXCP_DEBUG);
+                        /* Advance PC so that clearing the breakpoint will
+                           invalidate this TB.  */
+                        /* TODO: Advance PC by correct instruction length to
+                         * avoid disassembler error messages */
+                        dc->pc += 2;
+                        goto done_generating;
+                    }
+                    break;
                 }
             }
         }