NetBSD/sys/arch/sparc/sparc/db_interface.c

484 lines
9.9 KiB
C

/* $NetBSD: db_interface.c,v 1.33 2001/01/18 10:54:29 jdolecek Exp $ */
/*
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*
* From: db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
*/
/*
* Interface to new debugger.
*/
#include "opt_ddb.h" /* XXX ddb vs kgdb */
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <dev/cons.h>
#include <machine/db_machdep.h>
#include <ddb/db_access.h>
#if defined(DDB)
#include <ddb/db_command.h>
#include <ddb/db_sym.h>
#include <ddb/db_variables.h>
#include <ddb/db_extern.h>
#include <ddb/db_output.h>
#endif
#include <machine/instr.h>
#include <machine/bsd_openprom.h>
#include <machine/promlib.h>
#include <machine/ctlreg.h>
#include <sparc/sparc/asm.h>
#include "fb.h"
/*
* Read bytes from kernel address space for debugger.
*/
void
db_read_bytes(addr, size, data)
vaddr_t addr;
size_t size;
char *data;
{
char *src;
src = (char *)addr;
while (size-- > 0)
*data++ = *src++;
}
/*
* Write bytes to kernel address space for debugger.
*/
void
db_write_bytes(addr, size, data)
vaddr_t addr;
size_t size;
char *data;
{
extern char etext[];
char *dst;
dst = (char *)addr;
while (size-- > 0) {
if ((dst >= (char *)VM_MIN_KERNEL_ADDRESS) && (dst < etext))
pmap_writetext(dst, *data);
else
*dst = *data;
dst++, data++;
}
}
#if defined(DDB)
/*
* Data and functions used by DDB only.
*/
void
cpu_Debugger()
{
asm("ta 0x81");
}
static int nil;
const struct db_variable db_regs[] = {
{ "psr", (long *)&DDB_TF->tf_psr, FCN_NULL, },
{ "pc", (long *)&DDB_TF->tf_pc, FCN_NULL, },
{ "npc", (long *)&DDB_TF->tf_npc, FCN_NULL, },
{ "y", (long *)&DDB_TF->tf_y, FCN_NULL, },
{ "wim", (long *)&DDB_TF->tf_global[0], FCN_NULL, }, /* see reg.h */
{ "g0", (long *)&nil, FCN_NULL, },
{ "g1", (long *)&DDB_TF->tf_global[1], FCN_NULL, },
{ "g2", (long *)&DDB_TF->tf_global[2], FCN_NULL, },
{ "g3", (long *)&DDB_TF->tf_global[3], FCN_NULL, },
{ "g4", (long *)&DDB_TF->tf_global[4], FCN_NULL, },
{ "g5", (long *)&DDB_TF->tf_global[5], FCN_NULL, },
{ "g6", (long *)&DDB_TF->tf_global[6], FCN_NULL, },
{ "g7", (long *)&DDB_TF->tf_global[7], FCN_NULL, },
{ "o0", (long *)&DDB_TF->tf_out[0], FCN_NULL, },
{ "o1", (long *)&DDB_TF->tf_out[1], FCN_NULL, },
{ "o2", (long *)&DDB_TF->tf_out[2], FCN_NULL, },
{ "o3", (long *)&DDB_TF->tf_out[3], FCN_NULL, },
{ "o4", (long *)&DDB_TF->tf_out[4], FCN_NULL, },
{ "o5", (long *)&DDB_TF->tf_out[5], FCN_NULL, },
{ "o6", (long *)&DDB_TF->tf_out[6], FCN_NULL, },
{ "o7", (long *)&DDB_TF->tf_out[7], FCN_NULL, },
{ "l0", (long *)&DDB_FR->fr_local[0], FCN_NULL, },
{ "l1", (long *)&DDB_FR->fr_local[1], FCN_NULL, },
{ "l2", (long *)&DDB_FR->fr_local[2], FCN_NULL, },
{ "l3", (long *)&DDB_FR->fr_local[3], FCN_NULL, },
{ "l4", (long *)&DDB_FR->fr_local[4], FCN_NULL, },
{ "l5", (long *)&DDB_FR->fr_local[5], FCN_NULL, },
{ "l6", (long *)&DDB_FR->fr_local[6], FCN_NULL, },
{ "l7", (long *)&DDB_FR->fr_local[7], FCN_NULL, },
{ "i0", (long *)&DDB_FR->fr_arg[0], FCN_NULL, },
{ "i1", (long *)&DDB_FR->fr_arg[1], FCN_NULL, },
{ "i2", (long *)&DDB_FR->fr_arg[2], FCN_NULL, },
{ "i3", (long *)&DDB_FR->fr_arg[3], FCN_NULL, },
{ "i4", (long *)&DDB_FR->fr_arg[4], FCN_NULL, },
{ "i5", (long *)&DDB_FR->fr_arg[5], FCN_NULL, },
{ "i6", (long *)&DDB_FR->fr_arg[6], FCN_NULL, },
{ "i7", (long *)&DDB_FR->fr_arg[7], FCN_NULL, },
};
const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
extern label_t *db_recover;
int db_active = 0;
extern char *trap_type[];
void kdb_kbd_trap __P((struct trapframe *));
void db_prom_cmd __P((db_expr_t, int, db_expr_t, char *));
/*
* Received keyboard interrupt sequence.
*/
void
kdb_kbd_trap(tf)
struct trapframe *tf;
{
if (db_active == 0 && (boothowto & RB_KDB)) {
printf("\n\nkernel: keyboard interrupt\n");
kdb_trap(-1, tf);
}
}
/*
* kdb_trap - field a TRACE or BPT trap
*/
int
kdb_trap(type, tf)
int type;
struct trapframe *tf;
{
int s;
#if NFB > 0
fb_unblank();
#endif
/* While we're in the debugger, pause all other CPUs */
mp_pause_cpus();
switch (type) {
case T_BREAKPOINT: /* breakpoint */
case -1: /* keyboard interrupt */
break;
default:
printf("kernel: %s trap\n", trap_type[type & 0xff]);
if (db_recover != 0) {
db_error("Faulted in DDB; continuing...\n");
/*NOTREACHED*/
}
}
/* Should switch to kdb`s own stack here. */
ddb_regs.db_tf = *tf;
ddb_regs.db_fr = *(struct frame *)tf->tf_out[6];
s = splhigh();
db_active++;
cnpollc(TRUE);
db_trap(type, 0/*code*/);
cnpollc(FALSE);
db_active--;
splx(s);
*(struct frame *)tf->tf_out[6] = ddb_regs.db_fr;
*tf = ddb_regs.db_tf;
/* Other CPUs can continue now */
mp_resume_cpus();
return (1);
}
void
db_prom_cmd(addr, have_addr, count, modif)
db_expr_t addr;
int have_addr;
db_expr_t count;
char *modif;
{
prom_abort();
}
struct db_command sparc_db_command_table[] = {
{ "prom", db_prom_cmd, 0, 0 },
{ (char *)0, }
};
void
db_machine_init()
{
db_machine_commands_install(sparc_db_command_table);
}
#endif /* DDB */
/*
* support for SOFTWARE_SSTEP:
* return the next pc if the given branch is taken.
*
* note: in the case of conditional branches with annul,
* this actually returns the next pc in the "not taken" path,
* but in that case next_instr_address() will return the
* next pc in the "taken" path. so even tho the breakpoints
* are backwards, everything will still work, and the logic is
* much simpler this way.
*/
db_addr_t
db_branch_taken(inst, pc, regs)
int inst;
db_addr_t pc;
db_regs_t *regs;
{
union instr insn;
db_addr_t npc = ddb_regs.db_tf.tf_npc;
insn.i_int = inst;
/*
* if this is not an annulled conditional branch, the next pc is "npc".
*/
if (insn.i_any.i_op != IOP_OP2 || insn.i_branch.i_annul != 1)
return npc;
switch (insn.i_op2.i_op2) {
case IOP2_Bicc:
case IOP2_FBfcc:
case IOP2_BPcc:
case IOP2_FBPfcc:
case IOP2_CBccc:
/* branch on some condition-code */
switch (insn.i_branch.i_cond)
{
case Icc_A: /* always */
return pc + ((inst << 10) >> 8);
default: /* all other conditions */
return npc + 4;
}
case IOP2_BPr:
/* branch on register, always conditional */
return npc + 4;
default:
/* not a branch */
panic("branch_taken() on non-branch");
}
}
boolean_t
db_inst_branch(inst)
int inst;
{
union instr insn;
insn.i_int = inst;
if (insn.i_any.i_op != IOP_OP2)
return FALSE;
switch (insn.i_op2.i_op2) {
case IOP2_BPcc:
case IOP2_Bicc:
case IOP2_BPr:
case IOP2_FBPfcc:
case IOP2_FBfcc:
case IOP2_CBccc:
return TRUE;
default:
return FALSE;
}
}
boolean_t
db_inst_call(inst)
int inst;
{
union instr insn;
insn.i_int = inst;
switch (insn.i_any.i_op) {
case IOP_CALL:
return TRUE;
case IOP_reg:
return (insn.i_op3.i_op3 == IOP3_JMPL) && !db_inst_return(inst);
default:
return FALSE;
}
}
boolean_t
db_inst_unconditional_flow_transfer(inst)
int inst;
{
union instr insn;
insn.i_int = inst;
if (db_inst_call(inst))
return TRUE;
if (insn.i_any.i_op != IOP_OP2)
return FALSE;
switch (insn.i_op2.i_op2)
{
case IOP2_BPcc:
case IOP2_Bicc:
case IOP2_FBPfcc:
case IOP2_FBfcc:
case IOP2_CBccc:
return insn.i_branch.i_cond == Icc_A;
default:
return FALSE;
}
}
boolean_t
db_inst_return(inst)
int inst;
{
return (inst == I_JMPLri(I_G0, I_O7, 8) || /* ret */
inst == I_JMPLri(I_G0, I_I7, 8)); /* retl */
}
boolean_t
db_inst_trap_return(inst)
int inst;
{
union instr insn;
insn.i_int = inst;
return (insn.i_any.i_op == IOP_reg &&
insn.i_op3.i_op3 == IOP3_RETT);
}
int
db_inst_load(inst)
int inst;
{
union instr insn;
insn.i_int = inst;
if (insn.i_any.i_op != IOP_mem)
return 0;
switch (insn.i_op3.i_op3) {
case IOP3_LD:
case IOP3_LDUB:
case IOP3_LDUH:
case IOP3_LDD:
case IOP3_LDSB:
case IOP3_LDSH:
case IOP3_LDSTUB:
case IOP3_SWAP:
case IOP3_LDA:
case IOP3_LDUBA:
case IOP3_LDUHA:
case IOP3_LDDA:
case IOP3_LDSBA:
case IOP3_LDSHA:
case IOP3_LDSTUBA:
case IOP3_SWAPA:
case IOP3_LDF:
case IOP3_LDFSR:
case IOP3_LDDF:
case IOP3_LFC:
case IOP3_LDCSR:
case IOP3_LDDC:
return 1;
default:
return 0;
}
}
int
db_inst_store(inst)
int inst;
{
union instr insn;
insn.i_int = inst;
if (insn.i_any.i_op != IOP_mem)
return 0;
switch (insn.i_op3.i_op3) {
case IOP3_ST:
case IOP3_STB:
case IOP3_STH:
case IOP3_STD:
case IOP3_LDSTUB:
case IOP3_SWAP:
case IOP3_STA:
case IOP3_STBA:
case IOP3_STHA:
case IOP3_STDA:
case IOP3_LDSTUBA:
case IOP3_SWAPA:
case IOP3_STF:
case IOP3_STFSR:
case IOP3_STDFQ:
case IOP3_STDF:
case IOP3_STC:
case IOP3_STCSR:
case IOP3_STDCQ:
case IOP3_STDC:
return 1;
default:
return 0;
}
}