diff options
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/kernel/gdb-stub.c | 127 |
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 = *(®s->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) |