|
@@ -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__ */
|