HelenOS sources
This source file includes following definitions.
- is_jump
- write_inst
- cmd_add_breakpoint
- cmd_del_breakpoint
- cmd_print_breakpoints
- debugger_init
- debugger_bpoint
#include <arch/debugger.h>
#include <barrier.h>
#include <console/kconsole.h>
#include <console/cmd.h>
#include <stdio.h>
#include <log.h>
#include <panic.h>
#include <arch.h>
#include <arch/cp0.h>
#include <halt.h>
#include <symtab.h>
bpinfo_t breakpoints[BKPOINTS_MAX];
IRQ_SPINLOCK_STATIC_INITIALIZE(bkpoint_lock);
#ifdef CONFIG_KCONSOLE
static int cmd_print_breakpoints(cmd_arg_t *);
static int cmd_del_breakpoint(cmd_arg_t *);
static int cmd_add_breakpoint(cmd_arg_t *);
static cmd_info_t bkpts_info = {
.name = "bkpts",
.description = "Print breakpoint table.",
.func = cmd_print_breakpoints,
.argc = 0,
};
static cmd_arg_t del_argv = {
.type = ARG_TYPE_INT
};
static cmd_info_t delbkpt_info = {
.name = "delbkpt",
.description = "Delete breakpoint.",
.func = cmd_del_breakpoint,
.argc = 1,
.argv = &del_argv
};
static cmd_arg_t add_argv = {
.type = ARG_TYPE_INT
};
static cmd_info_t addbkpt_info = {
.name = "addbkpt",
.description = "Add bkpoint (break on j/branch insts unsupported).",
.func = cmd_add_breakpoint,
.argc = 1,
.argv = &add_argv
};
static cmd_arg_t adde_argv[] = {
{ .type = ARG_TYPE_INT },
{ .type = ARG_TYPE_INT }
};
static cmd_info_t addbkpte_info = {
.name = "addbkpte",
.description = "Add bkpoint with a trigger function.",
.func = cmd_add_breakpoint,
.argc = 2,
.argv = adde_argv
};
#endif
static struct {
uint32_t andmask;
uint32_t value;
} jmpinstr[] = {
{ 0xf3ff0000, 0x41000000 },
{ 0xf3ff0000, 0x41020000 },
{ 0xf3ff0000, 0x41010000 },
{ 0xf3ff0000, 0x41030000 },
{ 0xfc000000, 0x10000000 },
{ 0xfc000000, 0x50000000 },
{ 0xfc1f0000, 0x04010000 },
{ 0xfc1f0000, 0x04110000 },
{ 0xfc1f0000, 0x04130000 },
{ 0xfc1f0000, 0x04030000 },
{ 0xfc1f0000, 0x1c000000 },
{ 0xfc1f0000, 0x5c000000 },
{ 0xfc1f0000, 0x18000000 },
{ 0xfc1f0000, 0x58000000 },
{ 0xfc1f0000, 0x04000000 },
{ 0xfc1f0000, 0x04100000 },
{ 0xfc1f0000, 0x04120000 },
{ 0xfc1f0000, 0x04020000 },
{ 0xfc000000, 0x14000000 },
{ 0xfc000000, 0x54000000 },
{ 0xfc000000, 0x08000000 },
{ 0xfc000000, 0x0c000000 },
{ 0xfc1f07ff, 0x00000009 },
{ 0, 0 }
};
bool is_jump(sysarg_t instr)
{
unsigned int i;
for (i = 0; jmpinstr[i].andmask; i++) {
if ((instr & jmpinstr[i].andmask) == jmpinstr[i].value)
return true;
}
return false;
}
static inline void write_inst(uintptr_t addr, uint32_t inst)
{
*((uint32_t *) addr) = inst;
smc_coherence((uint32_t *) addr, 4);
}
#ifdef CONFIG_KCONSOLE
int cmd_add_breakpoint(cmd_arg_t *argv)
{
if (argv->intval & 0x3) {
printf("Not aligned instruction, forgot to use &symbol?\n");
return 1;
}
irq_spinlock_lock(&bkpoint_lock, true);
unsigned int i;
for (i = 0; i < BKPOINTS_MAX; i++) {
if (breakpoints[i].address == (uintptr_t) argv->intval) {
printf("Duplicate breakpoint %d.\n", i);
irq_spinlock_unlock(&bkpoint_lock, true);
return 0;
} else if ((breakpoints[i].address == (uintptr_t) argv->intval +
sizeof(sysarg_t)) || (breakpoints[i].address ==
(uintptr_t) argv->intval - sizeof(sysarg_t))) {
printf("Adjacent breakpoints not supported, conflict "
"with %d.\n", i);
irq_spinlock_unlock(&bkpoint_lock, true);
return 0;
}
}
bpinfo_t *cur = NULL;
for (i = 0; i < BKPOINTS_MAX; i++) {
if (!breakpoints[i].address) {
cur = &breakpoints[i];
break;
}
}
if (!cur) {
printf("Too many breakpoints.\n");
irq_spinlock_unlock(&bkpoint_lock, true);
return 0;
}
printf("Adding breakpoint on address %p\n", (void *) argv->intval);
cur->address = (uintptr_t) argv->intval;
cur->instruction = ((sysarg_t *) cur->address)[0];
cur->nextinstruction = ((sysarg_t *) cur->address)[1];
if (argv == &add_argv) {
cur->flags = 0;
} else {
cur->flags = BKPOINT_FUNCCALL;
cur->bkfunc = (void (*)(void *, istate_t *)) argv[1].intval;
}
if (is_jump(cur->instruction))
cur->flags |= BKPOINT_ONESHOT;
cur->counter = 0;
write_inst(cur->address, 0x0d);
irq_spinlock_unlock(&bkpoint_lock, true);
return 1;
}
int cmd_del_breakpoint(cmd_arg_t *argv)
{
if (argv->intval > BKPOINTS_MAX) {
printf("Invalid breakpoint number.\n");
return 0;
}
irq_spinlock_lock(&bkpoint_lock, true);
bpinfo_t *cur = &breakpoints[argv->intval];
if (!cur->address) {
printf("Breakpoint does not exist.\n");
irq_spinlock_unlock(&bkpoint_lock, true);
return 0;
}
if ((cur->flags & BKPOINT_INPROG) && (cur->flags & BKPOINT_ONESHOT)) {
printf("Cannot remove one-shot breakpoint in-progress\n");
irq_spinlock_unlock(&bkpoint_lock, true);
return 0;
}
write_inst(cur->address, cur->instruction);
write_inst(cur->address + 4, cur->nextinstruction);
cur->address = (uintptr_t) NULL;
irq_spinlock_unlock(&bkpoint_lock, true);
return 1;
}
int cmd_print_breakpoints(cmd_arg_t *argv)
{
unsigned int i;
printf("[nr] [count] [address ] [inprog] [oneshot] [funccall] [in symbol\n");
for (i = 0; i < BKPOINTS_MAX; i++) {
if (breakpoints[i].address) {
const char *symbol = symtab_fmt_name_lookup(
breakpoints[i].address);
printf("%-4u %7zu %p %-8s %-9s %-10s %s\n", i,
breakpoints[i].counter, (void *) breakpoints[i].address,
((breakpoints[i].flags & BKPOINT_INPROG) ? "true" :
"false"), ((breakpoints[i].flags & BKPOINT_ONESHOT) ?
"true" : "false"), ((breakpoints[i].flags &
BKPOINT_FUNCCALL) ? "true" : "false"), symbol);
}
}
return 1;
}
#endif
void debugger_init(void)
{
unsigned int i;
for (i = 0; i < BKPOINTS_MAX; i++)
breakpoints[i].address = (uintptr_t) NULL;
#ifdef CONFIG_KCONSOLE
cmd_initialize(&bkpts_info);
if (!cmd_register(&bkpts_info))
log(LF_OTHER, LVL_WARN, "Cannot register command %s",
bkpts_info.name);
cmd_initialize(&delbkpt_info);
if (!cmd_register(&delbkpt_info))
log(LF_OTHER, LVL_WARN, "Cannot register command %s",
delbkpt_info.name);
cmd_initialize(&addbkpt_info);
if (!cmd_register(&addbkpt_info))
log(LF_OTHER, LVL_WARN, "Cannot register command %s",
addbkpt_info.name);
cmd_initialize(&addbkpte_info);
if (!cmd_register(&addbkpte_info))
log(LF_OTHER, LVL_WARN, "Cannot register command %s",
addbkpte_info.name);
#endif
}
void debugger_bpoint(istate_t *istate)
{
if (cp0_cause_read() & 0x80000000)
panic("Breakpoint in branch delay slot not supported.");
irq_spinlock_lock(&bkpoint_lock, false);
bpinfo_t *cur = NULL;
uintptr_t fireaddr = istate->epc;
unsigned int i;
for (i = 0; i < BKPOINTS_MAX; i++) {
if ((fireaddr == breakpoints[i].address) &&
(!(breakpoints[i].flags & BKPOINT_REINST))) {
cur = &breakpoints[i];
break;
}
if ((breakpoints[i].flags & BKPOINT_REINST) &&
(fireaddr == breakpoints[i].address + sizeof(sysarg_t))) {
cur = &breakpoints[i];
break;
}
}
if (cur) {
if (cur->flags & BKPOINT_REINST) {
write_inst(cur->address, 0x0d);
write_inst(cur->address + 4, cur->nextinstruction);
cur->flags &= ~BKPOINT_REINST;
irq_spinlock_unlock(&bkpoint_lock, false);
return;
}
if (cur->flags & BKPOINT_INPROG)
printf("Warning: breakpoint recursion\n");
if (!(cur->flags & BKPOINT_FUNCCALL)) {
printf("***Breakpoint %u: %p in %s.\n", i,
(void *) fireaddr,
symtab_fmt_name_lookup(fireaddr));
}
write_inst(cur->address, cur->instruction);
if (!(cur->flags & BKPOINT_ONESHOT)) {
write_inst(cur->address + 4, 0x0d);
cur->flags |= BKPOINT_REINST;
}
cur->flags |= BKPOINT_INPROG;
} else {
printf("***Breakpoint %d: %p in %s.\n", i,
(void *) fireaddr,
symtab_fmt_name_lookup(fireaddr));
istate->epc += 4;
}
if (cur)
cur->counter++;
if (cur && (cur->flags & BKPOINT_FUNCCALL)) {
if (cur->bkfunc)
cur->bkfunc(cur, istate);
} else {
#ifdef CONFIG_KCONSOLE
atomic_store(&haltstate, 1);
irq_spinlock_unlock(&bkpoint_lock, false);
kconsole("debug", "Debug console ready.\n", false);
irq_spinlock_lock(&bkpoint_lock, false);
atomic_store(&haltstate, 0);
#endif
}
if ((cur) && (cur->address == fireaddr) &&
((cur->flags & BKPOINT_INPROG))) {
if ((cur->flags & BKPOINT_ONESHOT))
cur->address = (uintptr_t) NULL;
cur->flags &= ~BKPOINT_INPROG;
}
irq_spinlock_unlock(&bkpoint_lock, false);
}
HelenOS homepage, sources at GitHub