0,0 → 1,263 |
/* |
* Copyright (C) 2005 Ondrej Palkovsky |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
|
#include <arch/debugger.h> |
#include <memstr.h> |
#include <console/kconsole.h> |
#include <console/cmd.h> |
#include <symtab.h> |
#include <print.h> |
#include <panic.h> |
#include <arch.h> |
#include <arch/cp0.h> |
#include <func.h> |
|
bpinfo_t breakpoints[BKPOINTS_MAX]; |
spinlock_t bkpoint_lock; |
|
static int cmd_print_breakpoints(cmd_arg_t *argv); |
static cmd_info_t pbkpt_info = { |
.name = "pbkpt", |
.description = "Print breakpoint table.", |
.func = cmd_print_breakpoints, |
.argc = 0, |
}; |
|
static int cmd_del_breakpoint(cmd_arg_t *argv); |
static cmd_arg_t del_argv = { |
.type = ARG_TYPE_INT |
}; |
static cmd_info_t delbkpt_info = { |
.name = "delbkpt", |
.description = "delbkpt <number> - Delete breakpoint.", |
.func = cmd_del_breakpoint, |
.argc = 1, |
.argv = &del_argv |
}; |
|
static int cmd_add_breakpoint(cmd_arg_t *argv); |
static cmd_arg_t add_argv = { |
.type = ARG_TYPE_INT |
}; |
static cmd_info_t addbkpt_info = { |
.name = "addbkpt", |
.description = "addbkpt <&symbol> - new bkpoint. Break on J/Branch insts unsupported.", |
.func = cmd_add_breakpoint, |
.argc = 1, |
.argv = &add_argv |
}; |
|
/** Add new breakpoint to table */ |
int cmd_add_breakpoint(cmd_arg_t *argv) |
{ |
bpinfo_t *cur = NULL; |
ipl_t ipl; |
int i; |
|
if (argv->intval & 0x3) { |
printf("Not aligned instruction, forgot to use &symbol?\n"); |
return 1; |
} |
ipl = interrupts_disable(); |
spinlock_lock(&bkpoint_lock); |
|
/* Check, that the breakpoints do not conflict */ |
for (i=0; i<BKPOINTS_MAX; i++) { |
if (breakpoints[i].address == (__address)argv->intval) { |
printf("Duplicate breakpoint %d.\n", i); |
spinlock_unlock(&bkpoints_lock); |
return 0; |
} else if (breakpoints[i].address == (__address)argv->intval + sizeof(__native) || \ |
breakpoints[i].address == (__address)argv->intval - sizeof(__native)) { |
printf("Adjacent breakpoints not supported, conflict with %d.\n", i); |
spinlock_unlock(&bkpoints_lock); |
return 0; |
} |
|
} |
|
for (i=0; i<BKPOINTS_MAX; i++) |
if (!breakpoints[i].address) { |
cur = &breakpoints[i]; |
break; |
} |
if (!cur) { |
printf("Too many breakpoints.\n"); |
spinlock_unlock(&bkpoint_lock); |
interrupts_restore(ipl); |
return 0; |
} |
cur->address = (__address) argv->intval; |
printf("Adding breakpoint on address: %p\n", argv->intval); |
cur->instruction = ((__native *)cur->address)[0]; |
cur->nextinstruction = ((__native *)cur->address)[1]; |
cur->executing = false; |
|
/* Set breakpoint */ |
*((__native *)cur->address) = 0x0d; |
|
spinlock_unlock(&bkpoint_lock); |
interrupts_restore(ipl); |
|
return 1; |
} |
|
/** Remove breakpoint from table */ |
int cmd_del_breakpoint(cmd_arg_t *argv) |
{ |
bpinfo_t *cur; |
ipl_t ipl; |
|
if (argv->intval < 0 || argv->intval > BKPOINTS_MAX) { |
printf("Invalid breakpoint number.\n"); |
return 0; |
} |
ipl = interrupts_disable(); |
spinlock_lock(&bkpoint_lock); |
|
cur = &breakpoints[argv->intval]; |
if (!cur->address) { |
printf("Breakpoint does not exist.\n"); |
spinlock_unlock(&bkpoint_lock); |
interrupts_restore(ipl); |
return 0; |
} |
|
((__u32 *)cur->address)[0] = cur->instruction; |
((__u32 *)cur->address)[1] = cur->nextinstruction; |
|
cur->address = NULL; |
|
spinlock_unlock(&bkpoint_lock); |
interrupts_restore(ipl); |
return 1; |
} |
|
/** Print table of active breakpoints */ |
int cmd_print_breakpoints(cmd_arg_t *argv) |
{ |
int i; |
char *symbol; |
|
printf("Breakpoint table.\n"); |
for (i=0; i < BKPOINTS_MAX; i++) |
if (breakpoints[i].address) { |
symbol = get_symtab_entry(breakpoints[i].address); |
printf("%d. 0x%p in %s\n",i, |
breakpoints[i].address, symbol); |
} |
return 1; |
} |
|
/** Initialize debugger */ |
void debugger_init() |
{ |
int i; |
|
for (i=0; i<BKPOINTS_MAX; i++) |
breakpoints[i].address = NULL; |
spinlock_initialize(&bkpoint_lock, "breakpoint_lock"); |
|
cmd_initialize(&pbkpt_info); |
if (!cmd_register(&pbkpt_info)) |
panic("could not register command %s\n", pbkpt_info.name); |
|
cmd_initialize(&delbkpt_info); |
if (!cmd_register(&delbkpt_info)) |
panic("could not register command %s\n", delbkpt_info.name); |
|
cmd_initialize(&addbkpt_info); |
if (!cmd_register(&addbkpt_info)) |
panic("could not register command %s\n", addbkpt_info.name); |
} |
|
/** Handle breakpoint |
* |
* Find breakpoint in breakpoint table. |
* If found, call kconsole, set break on next instruction and reexecute. |
* If we are on "next instruction", set it back on the first and reexecute. |
* If breakpoint not found in breakpoint table, call kconsole and start |
* next instruction. |
*/ |
void debugger_bpoint(struct exception_regdump *pstate) |
{ |
char *symbol; |
bpinfo_t *cur = NULL; |
int i; |
|
symbol = get_symtab_entry(pstate->epc); |
|
|
/* test branch delay slot */ |
if (cp0_cause_read() & 0x80000000) |
panic("Breakpoint in branch delay slot not supported.\n"); |
|
spinlock_lock(&bkpoint_lock); |
for (i=0; i<BKPOINTS_MAX; i++) { |
if (pstate->epc == breakpoints[i].address || \ |
(pstate->epc == breakpoints[i].address+sizeof(__native) &&\ |
breakpoints[i].executing)) |
cur = &breakpoints[i]; |
break; |
|
} |
if (cur) { |
if ((cur->executing && pstate->epc==cur->address) || |
(!cur->executing && pstate->epc==cur->address+sizeof(__native))) |
panic("Weird breakpoint state.\n"); |
if (!cur->executing) { |
printf("***Breakpoint %d: %p in %s.\n", i, |
pstate->epc,symbol); |
/* Return first instruction back */ |
((__u32 *)cur->address)[0] = cur->instruction; |
/* Set Breakpoint on second */ |
((__u32 *)cur->address)[1] = 0x0d; |
cur->executing = true; |
} else { |
/* Set breakpoint on first instruction */ |
((__u32 *)cur->address)[0] = 0x0d; |
/* Return back the second */ |
((__u32 *)cur->address)[1] = cur->nextinstruction; |
cur->executing = false; |
spinlock_unlock(&bkpoint_lock); |
return; |
} |
} else { |
printf("***Breakpoint %p in %s.\n", pstate->epc, symbol); |
/* Move on to next instruction */ |
pstate->epc += 4; |
} |
spinlock_unlock(&bkpoint_lock); |
|
|
printf("***Type 'exit' to exit kconsole.\n"); |
/* Umm..we should rather set some 'debugstate' here */ |
haltstate = 1; |
kconsole("debug"); |
haltstate = 0; |
} |