summaryrefslogtreecommitdiffstats
path: root/arch/mips
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips')
-rw-r--r--arch/mips/kernel/gdb-stub.c127
1 files changed, 123 insertions, 4 deletions
diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c
index b327e3f37..8a2929e6a 100644
--- a/arch/mips/kernel/gdb-stub.c
+++ b/arch/mips/kernel/gdb-stub.c
@@ -12,7 +12,7 @@
*
* Copyright (C) 1995 Andreas Busse
*
- * $Id: gdb-stub.c,v 1.2 1997/07/01 08:59:06 ralf Exp $
+ * $Id: gdb-stub.c,v 1.3 1997/07/29 03:10:57 ralf Exp $
*/
/*
@@ -450,6 +450,106 @@ void show_gdbregs(struct gdb_regs * regs)
#endif /* dead code */
/*
+ * We single-step by setting breakpoints. When an exception
+ * is handled, we need to restore the instructions hoisted
+ * when the breakpoints were set.
+ *
+ * This is where we save the original instructions.
+ */
+static struct {
+ unsigned int addr;
+ unsigned int val;
+} step_bp[2];
+
+/*
+ * Set breakpoint instructions for single stepping.
+ */
+static void single_step(struct gdb_regs *regs)
+{
+ union mips_instruction insn;
+ unsigned int targ;
+ int is_branch, is_cond, i;
+
+#define BP 0x0000000d
+
+ targ = regs->cp0_epc;
+ insn.word = *(unsigned int *)targ;
+ is_branch = is_cond = 0;
+
+ switch (insn.i_format.opcode) {
+ /*
+ * jr and jalr are in r_format format.
+ */
+ case spec_op:
+ switch (insn.r_format.func) {
+ case jalr_op:
+ case jr_op:
+ targ = *(&regs->reg0 + insn.r_format.rs);
+ is_branch = 1;
+ break;
+ }
+ break;
+
+ /*
+ * This group contains:
+ * bltz_op, bgez_op, bltzl_op, bgezl_op,
+ * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
+ */
+ case bcond_op:
+ is_branch = is_cond = 1;
+ targ += 4 + (insn.i_format.simmediate << 2);
+ break;
+
+ /*
+ * These are unconditional and in j_format.
+ */
+ case jal_op:
+ case j_op:
+ is_branch = 1;
+ targ += 4;
+ targ >>= 28;
+ targ <<= 28;
+ targ |= (insn.j_format.target << 2);
+ break;
+
+ /*
+ * These are conditional.
+ */
+ case beq_op:
+ case beql_op:
+ case bne_op:
+ case bnel_op:
+ case blez_op:
+ case blezl_op:
+ case bgtz_op:
+ case bgtzl_op:
+ case cop0_op:
+ case cop1_op:
+ case cop2_op:
+ case cop1x_op:
+ is_branch = is_cond = 1;
+ targ += 4 + (insn.i_format.simmediate << 2);
+ break;
+ }
+
+ if (is_branch) {
+ i = 0;
+ if (is_cond && targ != (regs->cp0_epc + 8)) {
+ step_bp[i].addr = regs->cp0_epc + 8;
+ step_bp[i++].val = *(unsigned *)(regs->cp0_epc + 8);
+ *(unsigned *)(regs->cp0_epc + 8) = BP;
+ }
+ step_bp[i].addr = targ;
+ step_bp[i].val = *(unsigned *)targ;
+ *(unsigned *)targ = BP;
+ } else {
+ step_bp[0].addr = regs->cp0_epc + 4;
+ step_bp[0].val = *(unsigned *)(regs->cp0_epc + 4);
+ *(unsigned *)(regs->cp0_epc + 4) = BP;
+ }
+}
+
+/*
* This function does all command processing for interfacing to gdb. It
* returns 1 if you should skip the instruction at the trap address, 0
* otherwise.
@@ -490,6 +590,20 @@ void handle_exception (struct gdb_regs *regs)
if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst)
regs->cp0_epc += 4;
+ /*
+ * If we were single_stepping, restore the opcodes hoisted
+ * for the breakpoint[s].
+ */
+ if (step_bp[0].addr) {
+ *(unsigned *)step_bp[0].addr = step_bp[0].val;
+ step_bp[0].addr = 0;
+
+ if (step_bp[1].addr) {
+ *(unsigned *)step_bp[1].addr = step_bp[1].val;
+ step_bp[1].addr = 0;
+ }
+ }
+
stack = (long *)regs->reg29; /* stack ptr */
sigval = computeSignal(trap);
@@ -670,11 +784,16 @@ void handle_exception (struct gdb_regs *regs)
/*
* Step to next instruction
- * FIXME: Needs to be written
*/
case 's':
- strcpy (output_buffer, "S01");
- break;
+ /*
+ * There is no single step insn in the MIPS ISA, so we
+ * use breakpoints and continue, instead.
+ */
+ single_step(regs);
+ flush_cache_all();
+ return;
+ /* NOTREACHED */
/*
* Set baud rate (bBB)