summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@tilera.com>2010-10-14 16:32:41 -0400
committerChris Metcalf <cmetcalf@tilera.com>2010-10-15 15:38:26 -0400
commit233325b94999d4bb8df227bb39904a57509e4995 (patch)
tree1f195bded03ce5aa483b41531e739a8cc61ce392
parenta78c942df64ef4cf495fd4d8715e48501bd7f8a4 (diff)
downloadlinux-3.10-233325b94999d4bb8df227bb39904a57509e4995.tar.gz
linux-3.10-233325b94999d4bb8df227bb39904a57509e4995.tar.bz2
linux-3.10-233325b94999d4bb8df227bb39904a57509e4995.zip
arch/tile: enable single-step support for TILE-Gx
This is not quite the complete support, since we're not yet shipping intvec_64.S, but it is the support relevant to the set of files we are currently shipping, and makes it easier to track changes between our internal sources and our public GIT repository. Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
-rw-r--r--arch/tile/include/asm/traps.h4
-rw-r--r--arch/tile/kernel/intvec_32.S7
-rw-r--r--arch/tile/kernel/single_step.c73
-rw-r--r--arch/tile/kernel/traps.c2
4 files changed, 83 insertions, 3 deletions
diff --git a/arch/tile/include/asm/traps.h b/arch/tile/include/asm/traps.h
index 432a9c15c8a..d06e35f5720 100644
--- a/arch/tile/include/asm/traps.h
+++ b/arch/tile/include/asm/traps.h
@@ -59,4 +59,8 @@ void do_hardwall_trap(struct pt_regs *, int fault_num);
void do_breakpoint(struct pt_regs *, int fault_num);
+#ifdef __tilegx__
+void gx_singlestep_handle(struct pt_regs *, int fault_num);
+#endif
+
#endif /* _ASM_TILE_SYSCALLS_H */
diff --git a/arch/tile/kernel/intvec_32.S b/arch/tile/kernel/intvec_32.S
index 206dc7e1fe3..f5821626247 100644
--- a/arch/tile/kernel/intvec_32.S
+++ b/arch/tile/kernel/intvec_32.S
@@ -1472,7 +1472,12 @@ handle_ill:
lw r26, r24
sw r28, r26
- /* Clear TIF_SINGLESTEP */
+ /*
+ * Clear TIF_SINGLESTEP to prevent recursion if we execute an ill.
+ * The normal non-arch flow redundantly clears TIF_SINGLESTEP, but we
+ * need to clear it here and can't really impose on all other arches.
+ * So what's another write between friends?
+ */
GET_THREAD_INFO(r0)
addi r1, r0, THREAD_INFO_FLAGS_OFFSET
diff --git a/arch/tile/kernel/single_step.c b/arch/tile/kernel/single_step.c
index 5ec4b9c651f..1eb3b39e36c 100644
--- a/arch/tile/kernel/single_step.c
+++ b/arch/tile/kernel/single_step.c
@@ -15,7 +15,7 @@
* Derived from iLib's single-stepping code.
*/
-#ifndef __tilegx__ /* No support for single-step yet. */
+#ifndef __tilegx__ /* Hardware support for single step unavailable. */
/* These functions are only used on the TILE platform */
#include <linux/slab.h>
@@ -660,4 +660,75 @@ void single_step_once(struct pt_regs *regs)
regs->pc += 8;
}
+#else
+#include <linux/smp.h>
+#include <linux/ptrace.h>
+#include <arch/spr_def.h>
+
+static DEFINE_PER_CPU(unsigned long, ss_saved_pc);
+
+
+/*
+ * Called directly on the occasion of an interrupt.
+ *
+ * If the process doesn't have single step set, then we use this as an
+ * opportunity to turn single step off.
+ *
+ * It has been mentioned that we could conditionally turn off single stepping
+ * on each entry into the kernel and rely on single_step_once to turn it
+ * on for the processes that matter (as we already do), but this
+ * implementation is somewhat more efficient in that we muck with registers
+ * once on a bum interrupt rather than on every entry into the kernel.
+ *
+ * If SINGLE_STEP_CONTROL_K has CANCELED set, then an interrupt occurred,
+ * so we have to run through this process again before we can say that an
+ * instruction has executed.
+ *
+ * swint will set CANCELED, but it's a legitimate instruction. Fortunately
+ * it changes the PC. If it hasn't changed, then we know that the interrupt
+ * wasn't generated by swint and we'll need to run this process again before
+ * we can say an instruction has executed.
+ *
+ * If either CANCELED == 0 or the PC's changed, we send out SIGTRAPs and get
+ * on with our lives.
+ */
+
+void gx_singlestep_handle(struct pt_regs *regs, int fault_num)
+{
+ unsigned long *ss_pc = &__get_cpu_var(ss_saved_pc);
+ struct thread_info *info = (void *)current_thread_info();
+ int is_single_step = test_ti_thread_flag(info, TIF_SINGLESTEP);
+ unsigned long control = __insn_mfspr(SPR_SINGLE_STEP_CONTROL_K);
+
+ if (is_single_step == 0) {
+ __insn_mtspr(SPR_SINGLE_STEP_EN_K_K, 0);
+
+ } else if ((*ss_pc != regs->pc) ||
+ (!(control & SPR_SINGLE_STEP_CONTROL_1__CANCELED_MASK))) {
+
+ ptrace_notify(SIGTRAP);
+ control |= SPR_SINGLE_STEP_CONTROL_1__CANCELED_MASK;
+ control |= SPR_SINGLE_STEP_CONTROL_1__INHIBIT_MASK;
+ __insn_mtspr(SPR_SINGLE_STEP_CONTROL_K, control);
+ }
+}
+
+
+/*
+ * Called from need_singlestep. Set up the control registers and the enable
+ * register, then return back.
+ */
+
+void single_step_once(struct pt_regs *regs)
+{
+ unsigned long *ss_pc = &__get_cpu_var(ss_saved_pc);
+ unsigned long control = __insn_mfspr(SPR_SINGLE_STEP_CONTROL_K);
+
+ *ss_pc = regs->pc;
+ control |= SPR_SINGLE_STEP_CONTROL_1__CANCELED_MASK;
+ control |= SPR_SINGLE_STEP_CONTROL_1__INHIBIT_MASK;
+ __insn_mtspr(SPR_SINGLE_STEP_CONTROL_K, control);
+ __insn_mtspr(SPR_SINGLE_STEP_EN_K_K, 1 << USER_PL);
+}
+
#endif /* !__tilegx__ */
diff --git a/arch/tile/kernel/traps.c b/arch/tile/kernel/traps.c
index 7826a8b1799..5474fc2e77e 100644
--- a/arch/tile/kernel/traps.c
+++ b/arch/tile/kernel/traps.c
@@ -260,7 +260,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num,
address = regs->pc;
break;
case INT_UNALIGN_DATA:
-#ifndef __tilegx__ /* FIXME: GX: no single-step yet */
+#ifndef __tilegx__ /* Emulated support for single step debugging */
if (unaligned_fixup >= 0) {
struct single_step_state *state =
current_thread_info()->step_state;