patch-2.3.43 linux/arch/ia64/kdb/kdbsupport.c
Next file: linux/arch/ia64/kdb/pc_keyb.h
Previous file: linux/arch/ia64/kdb/kdb_traps.c
Back to the patch index
Back to the overall index
- Lines: 1311
- Date:
Tue Feb 8 12:01:59 2000
- Orig file:
v2.3.42/linux/arch/ia64/kdb/kdbsupport.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.42/linux/arch/ia64/kdb/kdbsupport.c linux/arch/ia64/kdb/kdbsupport.c
@@ -0,0 +1,1310 @@
+/*
+ * Minimalist Kernel Debugger
+ *
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ * Copyright (C) Scott Lurndal (slurn@engr.sgi.com)
+ * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com)
+ * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com)
+ * Copyright (C) David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc.
+ *
+ * Modifications from:
+ * Richard Bass 1999/07/20
+ * Many bug fixes and enhancements.
+ * Scott Foehner
+ * Port to ia64
+ * Srinivasa Thirumalachar
+ * RSE support for ia64
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/kdb.h>
+#include <linux/stddef.h>
+#include <linux/vmalloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/kdbsupport.h>
+#include <asm/rse.h>
+
+extern kdb_state_t kdb_state ;
+k_machreg_t dbregs[KDB_DBREGS];
+
+static int __init
+kdb_setup (char *str)
+{
+ kdb_flags |= KDB_FLAG_EARLYKDB;
+ return 1;
+}
+
+__setup("kdb", kdb_setup);
+
+static int
+kdb_ia64_sir (int argc, const char **argv, const char **envp, struct pt_regs *regs)
+{
+ u64 lid, tpr, lrr0, lrr1, itv, pmv, cmcv;
+
+ asm ("mov %0=cr.lid" : "=r"(lid));
+ asm ("mov %0=cr.tpr" : "=r"(tpr));
+ asm ("mov %0=cr.lrr0" : "=r"(lrr0));
+ asm ("mov %0=cr.lrr1" : "=r"(lrr1));
+ printk ("lid=0x%lx, tpr=0x%lx, lrr0=0x%lx, llr1=0x%lx\n", lid, tpr, lrr0, lrr1);
+
+ asm ("mov %0=cr.itv" : "=r"(itv));
+ asm ("mov %0=cr.pmv" : "=r"(pmv));
+ asm ("mov %0=cr.cmcv" : "=r"(cmcv));
+ printk ("itv=0x%lx, pmv=0x%lx, cmcv=0x%lx\n", itv, pmv, cmcv);
+
+ printk ("irr=0x%016lx,0x%016lx,0x%016lx,0x%016lx\n",
+ ia64_get_irr0(), ia64_get_irr1(), ia64_get_irr2(), ia64_get_irr3());
+ return 0;
+}
+
+void __init
+kdb_init (void)
+{
+ extern void kdb_inittab(void);
+ unsigned long reg;
+
+ kdb_inittab();
+ kdb_initbptab();
+#if 0
+ kdb_disinit();
+#endif
+ kdb_printf("kdb version %d.%d by Scott Lurndal. "\
+ "Copyright SGI, All Rights Reserved\n",
+ KDB_MAJOR_VERSION, KDB_MINOR_VERSION);
+
+ /* Enable debug registers */
+ __asm__ ("mov %0=psr":"=r"(reg));
+ reg |= IA64_PSR_DB;
+ __asm__ ("mov psr.l=%0"::"r"(reg));
+ ia64_srlz_d();
+
+ /* Init kdb state */
+ kdb_state.bkpt_handling_state = BKPTSTATE_NOT_HANDLED ;
+
+ kdb_register("irr", kdb_ia64_sir, "", "Show interrupt registers", 0);
+}
+
+/*
+ * kdbprintf
+ * kdbgetword
+ * kdb_getstr
+ */
+
+char *
+kbd_getstr(char *buffer, size_t bufsize, char *prompt)
+{
+ extern char* kdb_getscancode(char *, size_t);
+
+#if defined(CONFIG_SMP)
+ kdb_printf(prompt, smp_processor_id());
+#else
+ kdb_printf("%s", prompt);
+#endif
+
+ return kdb_getscancode(buffer, bufsize);
+
+}
+
+int
+kdb_printf(const char *fmt, ...)
+{
+ char buffer[256];
+ va_list ap;
+ int diag;
+ int linecount;
+
+ diag = kdbgetintenv("LINES", &linecount);
+ if (diag)
+ linecount = 22;
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+
+ printk("%s", buffer);
+#if 0
+ if (strchr(buffer, '\n') != NULL) {
+ kdb_nextline++;
+ }
+
+ if (kdb_nextline == linecount) {
+ char buf1[16];
+ char buf2[32];
+ extern char* kdb_getscancode(char *, size_t);
+ char *moreprompt;
+
+ /*
+ * Pause until cr.
+ */
+ moreprompt = kdbgetenv("MOREPROMPT");
+ if (moreprompt == NULL) {
+ moreprompt = "more> ";
+ }
+
+#if defined(CONFIG_SMP)
+ if (strchr(moreprompt, '%')) {
+ sprintf(buf2, moreprompt, smp_processor_id());
+ moreprompt = buf2;
+ }
+#endif
+
+ printk(moreprompt);
+ (void) kdb_getscancode(buf1, sizeof(buf1));
+
+ kdb_nextline = 1;
+
+ if ((buf1[0] == 'q')
+ || (buf1[0] == 'Q')) {
+ kdb_longjmp(&kdbjmpbuf, 1);
+ }
+ }
+#endif
+ return 0;
+}
+
+unsigned long
+kdbgetword(unsigned long addr, int width)
+{
+ /*
+ * This function checks the address for validity. Any address
+ * in the range PAGE_OFFSET to high_memory is legal, any address
+ * which maps to a vmalloc region is legal, and any address which
+ * is a user address, we use get_user() to verify validity.
+ */
+
+ if (addr < PAGE_OFFSET) {
+ /*
+ * Usermode address.
+ */
+ unsigned long diag;
+ unsigned long ulval;
+
+ switch (width) {
+ case 8:
+ { unsigned long *lp;
+
+ lp = (unsigned long *) addr;
+ diag = get_user(ulval, lp);
+ break;
+ }
+ case 4:
+ { unsigned int *ip;
+
+ ip = (unsigned int *) addr;
+ diag = get_user(ulval, ip);
+ break;
+ }
+ case 2:
+ { unsigned short *sp;
+
+ sp = (unsigned short *) addr;
+ diag = get_user(ulval, sp);
+ break;
+ }
+ case 1:
+ { unsigned char *cp;
+
+ cp = (unsigned char *) addr;
+ diag = get_user(ulval, cp);
+ break;
+ }
+ default:
+ printk("kdbgetword: Bad width\n");
+ return 0L;
+ }
+
+ if (diag) {
+ if ((kdb_flags & KDB_FLAG_SUPRESS) == 0) {
+ printk("kdb: Bad user address 0x%lx\n", addr);
+ kdb_flags |= KDB_FLAG_SUPRESS;
+ }
+ return 0L;
+ }
+ kdb_flags &= ~KDB_FLAG_SUPRESS;
+ return ulval;
+ }
+
+ if (addr > (unsigned long)high_memory) {
+ extern int kdb_vmlist_check(unsigned long, unsigned long);
+
+ if (!kdb_vmlist_check(addr, addr+width)) {
+ /*
+ * Would appear to be an illegal kernel address;
+ * Print a message once, and don't print again until
+ * a legal address is used.
+ */
+ if ((kdb_flags & KDB_FLAG_SUPRESS) == 0) {
+ printk("kdb: Bad kernel address 0x%lx\n", addr);
+ kdb_flags |= KDB_FLAG_SUPRESS;
+ }
+ return 0L;
+ }
+ }
+
+ /*
+ * A good address. Reset error flag.
+ */
+ kdb_flags &= ~KDB_FLAG_SUPRESS;
+
+ switch (width) {
+ case 8:
+ { unsigned long *lp;
+
+ lp = (unsigned long *)(addr);
+ return *lp;
+ }
+ case 4:
+ { unsigned int *ip;
+
+ ip = (unsigned int *)(addr);
+ return *ip;
+ }
+ case 2:
+ { unsigned short *sp;
+
+ sp = (unsigned short *)(addr);
+ return *sp;
+ }
+ case 1:
+ { unsigned char *cp;
+
+ cp = (unsigned char *)(addr);
+ return *cp;
+ }
+ }
+
+ printk("kdbgetword: Bad width\n");
+ return 0L;
+}
+
+/*
+ * Start of breakpoint management routines
+ */
+
+/*
+ * Arg: bp structure
+ */
+
+int
+kdb_allocdbreg(kdb_bp_t *bp)
+{
+ int i=0;
+
+ /* For inst bkpt, just return. No hw reg alloc to be done. */
+
+ if (bp->bp_mode == BKPTMODE_INST) {
+ return i;
+ } else if (bp->bp_mode == BKPTMODE_DATAW) {
+ for(i=0; i<KDB_DBREGS; i++) {
+ if (dbregs[i] == 0xffffffff) {
+ dbregs[i] = 0;
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+void
+kdb_freedbreg(kdb_bp_t *bp)
+{
+ if (bp->bp_mode == BKPTMODE_DATAW)
+ dbregs[bp->bp_reg] = 0xffffffff;
+}
+
+void
+kdb_initdbregs(void)
+{
+ int i;
+
+ for(i=0; i<KDB_DBREGS; i++) {
+ dbregs[i] = 0xffffffff;
+ }
+}
+int
+kdbinstalltrap(int type, handler_t newh, handler_t *oldh)
+{
+ /*
+ * Usurp INTn. XXX - TBD.
+ */
+
+ return 0;
+}
+
+int
+install_instbkpt(kdb_bp_t *bp)
+{
+ unsigned long *addr = (unsigned long *)bp->bp_addr ;
+ bundle_t *bundle = (bundle_t *)bp->bp_longinst;
+
+ /* save current bundle */
+ *bundle = *(bundle_t *)addr ;
+
+ /* Set the break point! */
+ ((bundle_t *)addr)->lform.low8 = (
+ (((bundle_t *)addr)->lform.low8 & ~INST_SLOT0_MASK) |
+ BREAK_INSTR);
+
+ /* set flag */
+ bp->bp_instvalid = 1 ;
+
+ /* flush icache as it is stale now */
+ ia64_flush_icache_page((unsigned long)addr) ;
+
+#ifdef KDB_DEBUG
+ kdb_printf ("[0x%016lx]: install 0x%016lx with 0x%016lx\n",
+ addr, bundle->lform.low8, addr[0]) ;
+#endif
+ return 0 ;
+}
+
+int
+install_databkpt(kdb_bp_t *bp)
+{
+ unsigned long dbreg_addr = bp->bp_reg * 2;
+ unsigned long dbreg_cond = dbreg_addr + 1;
+ unsigned long value = 0x8fffffffffffffff;
+ unsigned long addr = (unsigned long)bp->bp_addr;
+ __asm__ ("mov dbr[%0]=%1"::"r"(dbreg_cond),"r"(value));
+// __asm__ ("movl %0,%%db0\n\t"::"r"(contents));
+ __asm__ ("mov dbr[%0]=%1"::"r"(dbreg_addr),"r"(addr));
+ ia64_insn_group_barrier();
+ ia64_srlz_i();
+ ia64_insn_group_barrier();
+
+#ifdef KDB_DEBUG
+ kdb_printf("installed dbkpt at 0x%016lx\n", addr) ;
+#endif
+ return 0;
+}
+
+int
+kdbinstalldbreg(kdb_bp_t *bp)
+{
+ if (bp->bp_mode == BKPTMODE_INST) {
+ return install_instbkpt(bp) ;
+ } else if (bp->bp_mode == BKPTMODE_DATAW) {
+ return install_databkpt(bp) ;
+ }
+ return 0;
+}
+
+void
+remove_instbkpt(kdb_bp_t *bp)
+{
+ unsigned long *addr = (unsigned long *)bp->bp_addr ;
+ bundle_t *bundle = (bundle_t *)bp->bp_longinst;
+
+ if (!bp->bp_instvalid)
+ /* Nothing to remove. If we just alloced the bkpt
+ * but never resumed, the bp_inst will not be valid. */
+ return ;
+
+#ifdef KDB_DEBUG
+ kdb_printf ("[0x%016lx]: remove 0x%016lx with 0x%016lx\n",
+ addr, addr[0], bundle->lform.low8) ;
+#endif
+
+ /* restore current bundle */
+ *(bundle_t *)addr = *bundle ;
+ /* reset the flag */
+ bp->bp_instvalid = 0 ;
+ ia64_flush_icache_page((unsigned long)addr) ;
+}
+
+void
+remove_databkpt(kdb_bp_t *bp)
+{
+ int regnum = bp->bp_reg ;
+ unsigned long dbreg_addr = regnum * 2;
+ unsigned long dbreg_cond = dbreg_addr + 1;
+ unsigned long value = 0x0fffffffffffffff;
+ __asm__ ("mov dbr[%0]=%1"::"r"(dbreg_cond),"r"(value));
+// __asm__ ("movl %0,%%db0\n\t"::"r"(contents));
+ ia64_insn_group_barrier();
+ ia64_srlz_i();
+ ia64_insn_group_barrier();
+
+#ifdef KDB_DEBUG
+ kdb_printf("removed dbkpt at 0x%016lx\n", bp->bp_addr) ;
+#endif
+}
+
+void
+kdbremovedbreg(kdb_bp_t *bp)
+{
+ if (bp->bp_mode == BKPTMODE_INST) {
+ remove_instbkpt(bp) ;
+ } else if (bp->bp_mode == BKPTMODE_DATAW) {
+ remove_databkpt(bp) ;
+ }
+}
+
+k_machreg_t
+kdb_getdr6(void)
+{
+ return kdb_getdr(6);
+}
+
+k_machreg_t
+kdb_getdr7(void)
+{
+ return kdb_getdr(7);
+}
+
+k_machreg_t
+kdb_getdr(int regnum)
+{
+ k_machreg_t contents = 0;
+ unsigned long reg = (unsigned long)regnum;
+
+ __asm__ ("mov %0=ibr[%1]"::"r"(contents),"r"(reg));
+// __asm__ ("mov ibr[%0]=%1"::"r"(dbreg_cond),"r"(value));
+
+ return contents;
+}
+
+
+k_machreg_t
+kdb_getcr(int regnum)
+{
+ k_machreg_t contents = 0;
+ return contents;
+}
+
+void
+kdb_putdr6(k_machreg_t contents)
+{
+ kdb_putdr(6, contents);
+}
+
+void
+kdb_putdr7(k_machreg_t contents)
+{
+ kdb_putdr(7, contents);
+}
+
+void
+kdb_putdr(int regnum, k_machreg_t contents)
+{
+}
+
+void
+get_fault_regs(fault_regs_t *fr)
+{
+ fr->ifa = 0 ;
+ fr->isr = 0 ;
+
+ __asm__ ("rsm psr.ic;;") ;
+ ia64_srlz_d();
+ __asm__ ("mov %0=cr.ifa" : "=r"(fr->ifa));
+ __asm__ ("mov %0=cr.isr" : "=r"(fr->isr));
+ __asm__ ("ssm psr.ic;;") ;
+ ia64_srlz_d();
+}
+
+/*
+ * kdb_db_trap
+ *
+ * Perform breakpoint processing upon entry to the
+ * processor debugger fault. Determine and print
+ * the active breakpoint.
+ *
+ * Parameters:
+ * ef Exception frame containing machine register state
+ * reason Why did we enter kdb - fault or break
+ * Outputs:
+ * None.
+ * Returns:
+ * 0 Standard instruction or data breakpoint encountered
+ * 1 Single Step fault ('ss' command)
+ * 2 Single Step fault, caller should continue ('ssb' command)
+ * Locking:
+ * None.
+ * Remarks:
+ * Yup, there be goto's here.
+ */
+
+int
+kdb_db_trap(struct pt_regs *ef, int reason)
+{
+ int i, rv=0;
+
+ /* Trying very hard to not change the interface to kdb.
+ * So, eventhough we have these values in the fault function
+ * it is not passed in but read again.
+ */
+ fault_regs_t faultregs ;
+
+ if (reason == KDB_REASON_FLTDBG)
+ get_fault_regs(&faultregs) ;
+
+ /* NOTE : XXX: This has to be done only for data bkpts */
+ /* Prevent it from continuously faulting */
+ ef->cr_ipsr |= 0x0000002000000000;
+
+ if (ef->cr_ipsr & 0x0000010000000000) {
+ /* single step */
+ ef->cr_ipsr &= 0xfffffeffffffffff;
+ if ((kdb_state.bkpt_handling_state == BKPTSTATE_HANDLED)
+ && (kdb_state.cmd_given == CMDGIVEN_GO))
+ ;
+ else
+ kdb_printf("SS trap at 0x%lx\n", ef->cr_iip + ia64_psr(ef)->ri);
+ rv = 1;
+ kdb_state.reason_for_entry = ENTRYREASON_SSTEP ;
+ goto handled;
+ } else
+ kdb_state.reason_for_entry = ENTRYREASON_GO ;
+
+ /*
+ * Determine which breakpoint was encountered.
+ */
+ for(i=0; i<KDB_MAXBPT; i++) {
+ if ((breakpoints[i].bp_enabled)
+ && ((breakpoints[i].bp_addr == ef->cr_iip) ||
+ ((faultregs.ifa) &&
+ (breakpoints[i].bp_addr == faultregs.ifa)))) {
+ /*
+ * Hit this breakpoint. Remove it while we are
+ * handling hit to avoid recursion. XXX ??
+ */
+ if (breakpoints[i].bp_addr == faultregs.ifa)
+ kdb_printf("Data breakpoint #%d for 0x%lx at 0x%lx\n",
+ i, breakpoints[i].bp_addr, ef->cr_iip + ia64_psr(ef)->ri);
+ else
+ kdb_printf("%s breakpoint #%d at 0x%lx\n",
+ rwtypes[0],
+ i, breakpoints[i].bp_addr);
+
+ /*
+ * For an instruction breakpoint, disassemble
+ * the current instruction.
+ */
+#if 0
+ if (rw == 0) {
+ kdb_id1(ef->eip);
+ }
+#endif
+
+ goto handled;
+ }
+ }
+
+#if 0
+unknown:
+#endif
+ kdb_printf("Unknown breakpoint. Should forward. \n");
+ /* Need a flag for this. The skip should be done XXX
+ * when a go or single step command is done for this session.
+ * For now it is here.
+ */
+ ia64_increment_ip(ef) ;
+ return rv ;
+
+handled:
+
+ /* We are here after handling a break inst/data bkpt */
+ if (kdb_state.bkpt_handling_state == BKPTSTATE_NOT_HANDLED) {
+ kdb_state.bkpt_handling_state = BKPTSTATE_HANDLED ;
+ if (kdb_state.reason_for_entry == ENTRYREASON_GO) {
+ kdb_setsinglestep(ef) ;
+ kdb_state.kdb_action = ACTION_NOBPINSTALL;
+ /* We dont want bp install just this once */
+ kdb_state.cmd_given = CMDGIVEN_UNKNOWN ;
+ }
+ } else if (kdb_state.bkpt_handling_state == BKPTSTATE_HANDLED) {
+ kdb_state.bkpt_handling_state = BKPTSTATE_NOT_HANDLED ;
+ if (kdb_state.reason_for_entry == ENTRYREASON_SSTEP) {
+ if (kdb_state.cmd_given == CMDGIVEN_GO)
+ kdb_state.kdb_action = ACTION_NOPROMPT ;
+ kdb_state.cmd_given = CMDGIVEN_UNKNOWN ;
+ }
+ } else
+ kdb_printf("Unknown value of bkpt state\n") ;
+
+ return rv;
+
+}
+
+void
+kdb_setsinglestep(struct pt_regs *regs)
+{
+ regs->cr_ipsr |= 0x0000010000000000;
+#if 0
+ regs->eflags |= EF_TF;
+#endif
+}
+
+/*
+ * Symbol table functions.
+ */
+
+/*
+ * kdbgetsym
+ *
+ * Return the symbol table entry for the given symbol
+ *
+ * Parameters:
+ * symname Character string containing symbol name
+ * Outputs:
+ * Returns:
+ * NULL Symbol doesn't exist
+ * ksp Pointer to symbol table entry
+ * Locking:
+ * None.
+ * Remarks:
+ */
+
+__ksymtab_t *
+kdbgetsym(const char *symname)
+{
+ __ksymtab_t *ksp = __kdbsymtab;
+ int i;
+
+ if (symname == NULL)
+ return NULL;
+
+ for (i=0; i<__kdbsymtabsize; i++, ksp++) {
+ if (ksp->name && (strcmp(ksp->name, symname)==0)) {
+ return ksp;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * kdbgetsymval
+ *
+ * Return the address of the given symbol.
+ *
+ * Parameters:
+ * symname Character string containing symbol name
+ * Outputs:
+ * Returns:
+ * 0 Symbol name is NULL
+ * addr Address corresponding to symname
+ * Locking:
+ * None.
+ * Remarks:
+ */
+
+unsigned long
+kdbgetsymval(const char *symname)
+{
+ __ksymtab_t *ksp = kdbgetsym(symname);
+
+ return (ksp?ksp->value:0);
+}
+
+/*
+ * kdbaddmodsym
+ *
+ * Add a symbol to the kernel debugger symbol table. Called when
+ * a new module is loaded into the kernel.
+ *
+ * Parameters:
+ * symname Character string containing symbol name
+ * value Value of symbol
+ * Outputs:
+ * Returns:
+ * 0 Successfully added to table.
+ * 1 Duplicate symbol
+ * 2 Symbol table full
+ * Locking:
+ * None.
+ * Remarks:
+ */
+
+int
+kdbaddmodsym(char *symname, unsigned long value)
+{
+
+ /*
+ * Check for duplicate symbols.
+ */
+ if (kdbgetsym(symname)) {
+ printk("kdb: Attempt to register duplicate symbol '%s' @ 0x%lx\n",
+ symname, value);
+ return 1;
+ }
+
+ if (__kdbsymtabsize < __kdbmaxsymtabsize) {
+ __ksymtab_t *ksp = &__kdbsymtab[__kdbsymtabsize++];
+
+ ksp->name = symname;
+ ksp->value = value;
+ return 0;
+ }
+
+ /*
+ * No room left in kernel symbol table.
+ */
+ {
+ static int __kdbwarn = 0;
+
+ if (__kdbwarn == 0) {
+ __kdbwarn++;
+ printk("kdb: Exceeded symbol table size. Increase CONFIG_KDB_SYMTAB_SIZE in kernel configuration\n");
+ }
+ }
+
+ return 2;
+}
+
+/*
+ * kdbdelmodsym
+ *
+ * Add a symbol to the kernel debugger symbol table. Called when
+ * a new module is loaded into the kernel.
+ *
+ * Parameters:
+ * symname Character string containing symbol name
+ * value Value of symbol
+ * Outputs:
+ * Returns:
+ * 0 Successfully added to table.
+ * 1 Symbol not found
+ * Locking:
+ * None.
+ * Remarks:
+ */
+
+int
+kdbdelmodsym(const char *symname)
+{
+ __ksymtab_t *ksp, *endksp;
+
+ if (symname == NULL)
+ return 1;
+
+ /*
+ * Search for the symbol. If found, move
+ * all successive symbols down one position
+ * in the symbol table to avoid leaving holes.
+ */
+ endksp = &__kdbsymtab[__kdbsymtabsize];
+ for (ksp = __kdbsymtab; ksp < endksp; ksp++) {
+ if (ksp->name && (strcmp(ksp->name, symname) == 0)) {
+ endksp--;
+ for ( ; ksp < endksp; ksp++) {
+ *ksp = *(ksp + 1);
+ }
+ __kdbsymtabsize--;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * kdbnearsym
+ *
+ * Return the name of the symbol with the nearest address
+ * less than 'addr'.
+ *
+ * Parameters:
+ * addr Address to check for symbol near
+ * Outputs:
+ * Returns:
+ * NULL No symbol with address less than 'addr'
+ * symbol Returns the actual name of the symbol.
+ * Locking:
+ * None.
+ * Remarks:
+ */
+
+char *
+kdbnearsym(unsigned long addr)
+{
+ __ksymtab_t *ksp = __kdbsymtab;
+ __ksymtab_t *kpp = NULL;
+ int i;
+
+ for(i=0; i<__kdbsymtabsize; i++, ksp++) {
+ if (!ksp->name)
+ continue;
+
+ if (addr == ksp->value) {
+ kpp = ksp;
+ break;
+ }
+ if (addr > ksp->value) {
+ if ((kpp == NULL)
+ || (ksp->value > kpp->value)) {
+ kpp = ksp;
+ }
+ }
+ }
+
+ /*
+ * If more than 128k away, don't bother.
+ */
+ if ((kpp == NULL)
+ || ((addr - kpp->value) > 0x20000)) {
+ return NULL;
+ }
+
+ return kpp->name;
+}
+
+/*
+ * kdbgetregcontents
+ *
+ * Return the contents of the register specified by the
+ * input string argument. Return an error if the string
+ * does not match a machine register.
+ *
+ * The following pseudo register names are supported:
+ * ®s - Prints address of exception frame
+ * kesp - Prints kernel stack pointer at time of fault
+ * sstk - Prints switch stack for ia64
+ * %<regname> - Uses the value of the registers at the
+ * last time the user process entered kernel
+ * mode, instead of the registers at the time
+ * kdb was entered.
+ *
+ * Parameters:
+ * regname Pointer to string naming register
+ * regs Pointer to structure containing registers.
+ * Outputs:
+ * *contents Pointer to unsigned long to recieve register contents
+ * Returns:
+ * 0 Success
+ * KDB_BADREG Invalid register name
+ * Locking:
+ * None.
+ * Remarks:
+ *
+ * Note that this function is really machine independent. The kdb
+ * register list is not, however.
+ */
+
+static struct kdbregs {
+ char *reg_name;
+ size_t reg_offset;
+} kdbreglist[] = {
+ { " psr", offsetof(struct pt_regs, cr_ipsr) },
+ { " ifs", offsetof(struct pt_regs, cr_ifs) },
+ { " ip", offsetof(struct pt_regs, cr_iip) },
+
+ { "unat", offsetof(struct pt_regs, ar_unat) },
+ { " pfs", offsetof(struct pt_regs, ar_pfs) },
+ { " rsc", offsetof(struct pt_regs, ar_rsc) },
+
+ { "rnat", offsetof(struct pt_regs, ar_rnat) },
+ { "bsps", offsetof(struct pt_regs, ar_bspstore) },
+ { " pr", offsetof(struct pt_regs, pr) },
+
+ { "ldrs", offsetof(struct pt_regs, loadrs) },
+ { " ccv", offsetof(struct pt_regs, ar_ccv) },
+ { "fpsr", offsetof(struct pt_regs, ar_fpsr) },
+
+ { " b0", offsetof(struct pt_regs, b0) },
+ { " b6", offsetof(struct pt_regs, b6) },
+ { " b7", offsetof(struct pt_regs, b7) },
+
+ { " r1",offsetof(struct pt_regs, r1) },
+ { " r2",offsetof(struct pt_regs, r2) },
+ { " r3",offsetof(struct pt_regs, r3) },
+
+ { " r8",offsetof(struct pt_regs, r8) },
+ { " r9",offsetof(struct pt_regs, r9) },
+ { " r10",offsetof(struct pt_regs, r10) },
+
+ { " r11",offsetof(struct pt_regs, r11) },
+ { " r12",offsetof(struct pt_regs, r12) },
+ { " r13",offsetof(struct pt_regs, r13) },
+
+ { " r14",offsetof(struct pt_regs, r14) },
+ { " r15",offsetof(struct pt_regs, r15) },
+ { " r16",offsetof(struct pt_regs, r16) },
+
+ { " r17",offsetof(struct pt_regs, r17) },
+ { " r18",offsetof(struct pt_regs, r18) },
+ { " r19",offsetof(struct pt_regs, r19) },
+
+ { " r20",offsetof(struct pt_regs, r20) },
+ { " r21",offsetof(struct pt_regs, r21) },
+ { " r22",offsetof(struct pt_regs, r22) },
+
+ { " r23",offsetof(struct pt_regs, r23) },
+ { " r24",offsetof(struct pt_regs, r24) },
+ { " r25",offsetof(struct pt_regs, r25) },
+
+ { " r26",offsetof(struct pt_regs, r26) },
+ { " r27",offsetof(struct pt_regs, r27) },
+ { " r28",offsetof(struct pt_regs, r28) },
+
+ { " r29",offsetof(struct pt_regs, r29) },
+ { " r30",offsetof(struct pt_regs, r30) },
+ { " r31",offsetof(struct pt_regs, r31) },
+
+};
+
+static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs);
+
+int
+kdbgetregcontents(const char *regname,
+ struct pt_regs *regs,
+ unsigned long *contents)
+{
+ int i;
+
+ if (strcmp(regname, "®s") == 0) {
+ *contents = (unsigned long)regs;
+ return 0;
+ }
+
+ if (strcmp(regname, "sstk") == 0) {
+ *contents = (unsigned long)getprsregs(regs) ;
+ return 0;
+ }
+
+ if (strcmp(regname, "isr") == 0) {
+ fault_regs_t fr ;
+ get_fault_regs(&fr) ;
+ *contents = fr.isr ;
+ return 0 ;
+ }
+
+#if 0
+ /* XXX need to verify this */
+ if (strcmp(regname, "kesp") == 0) {
+ *contents = (unsigned long)regs + sizeof(struct pt_regs);
+ return 0;
+ }
+
+ if (regname[0] == '%') {
+ /* User registers: %%e[a-c]x, etc */
+ regname++;
+ regs = (struct pt_regs *)
+ (current->thread.ksp - sizeof(struct pt_regs));
+ }
+#endif
+
+ for (i=0; i<nkdbreglist; i++) {
+ if (strstr(kdbreglist[i].reg_name, regname))
+ break;
+ }
+
+ if (i == nkdbreglist) {
+ /* Lets check the rse maybe */
+ if (regname[0] == 'r')
+ if (show_cur_stack_frame(regs, simple_strtoul(regname+1, 0, 0) - 31,
+ contents))
+ return 0 ;
+ return KDB_BADREG;
+ }
+
+ *contents = *(unsigned long *)((unsigned long)regs +
+ kdbreglist[i].reg_offset);
+
+ return 0;
+}
+
+/*
+ * kdbsetregcontents
+ *
+ * Set the contents of the register specified by the
+ * input string argument. Return an error if the string
+ * does not match a machine register.
+ *
+ * Supports modification of user-mode registers via
+ * %<register-name>
+ *
+ * Parameters:
+ * regname Pointer to string naming register
+ * regs Pointer to structure containing registers.
+ * contents Unsigned long containing new register contents
+ * Outputs:
+ * Returns:
+ * 0 Success
+ * KDB_BADREG Invalid register name
+ * Locking:
+ * None.
+ * Remarks:
+ */
+
+int
+kdbsetregcontents(const char *regname,
+ struct pt_regs *regs,
+ unsigned long contents)
+{
+ int i;
+
+ if (regname[0] == '%') {
+ regname++;
+ regs = (struct pt_regs *)
+ (current->thread.ksp - sizeof(struct pt_regs));
+ }
+
+ for (i=0; i<nkdbreglist; i++) {
+ if (strnicmp(kdbreglist[i].reg_name,
+ regname,
+ strlen(regname)) == 0)
+ break;
+ }
+
+ if ((i == nkdbreglist)
+ || (strlen(kdbreglist[i].reg_name) != strlen(regname))) {
+ return KDB_BADREG;
+ }
+
+ *(unsigned long *)((unsigned long)regs + kdbreglist[i].reg_offset) =
+ contents;
+
+ return 0;
+}
+
+/*
+ * kdbdumpregs
+ *
+ * Dump the specified register set to the display.
+ *
+ * Parameters:
+ * regs Pointer to structure containing registers.
+ * type Character string identifying register set to dump
+ * extra string further identifying register (optional)
+ * Outputs:
+ * Returns:
+ * 0 Success
+ * Locking:
+ * None.
+ * Remarks:
+ * This function will dump the general register set if the type
+ * argument is NULL (struct pt_regs). The alternate register
+ * set types supported by this function:
+ *
+ * d Debug registers
+ * c Control registers
+ * u User registers at most recent entry to kernel
+ * Following not yet implemented:
+ * m Model Specific Registers (extra defines register #)
+ * r Memory Type Range Registers (extra defines register)
+ *
+ * For now, all registers are covered as follows:
+ *
+ * rd - dumps all regs
+ * rd %isr - current interrupt status reg, read freshly
+ * rd s - valid stacked regs
+ * rd %sstk - gets switch stack addr. dump memory and search
+ * rd d - debug regs, may not be too useful
+ *
+ * ARs TB Done
+ * Interrupt regs TB Done ??
+ * OTHERS TB Decided ??
+ *
+ * Intel wish list
+ * These will be implemented later - Srinivasa
+ *
+ * type action
+ * ---- ------
+ * g dump all General static registers
+ * s dump all general Stacked registers
+ * f dump all Floating Point registers
+ * p dump all Predicate registers
+ * b dump all Branch registers
+ * a dump all Application registers
+ * c dump all Control registers
+ *
+ */
+
+int
+kdbdumpregs(struct pt_regs *regs,
+ const char *type,
+ const char *extra)
+
+{
+ int i;
+ int count = 0;
+
+ if (type
+ && (type[0] == 'u')) {
+ type = NULL;
+ regs = (struct pt_regs *)
+ (current->thread.ksp - sizeof(struct pt_regs));
+ }
+
+ if (type == NULL) {
+ for (i=0; i<nkdbreglist; i++) {
+ kdb_printf("%s: 0x%16.16lx ",
+ kdbreglist[i].reg_name,
+ *(unsigned long *)((unsigned long)regs +
+ kdbreglist[i].reg_offset));
+
+ if ((++count % 3) == 0)
+ kdb_printf("\n");
+ }
+
+ kdb_printf("®s = 0x%16.16lx\n", regs);
+
+ return 0;
+ }
+
+ switch (type[0]) {
+ case 'd':
+ {
+ for(i=0; i<8; i+=2) {
+ kdb_printf("idr%d: 0x%16.16lx idr%d: 0x%16.16lx\n", i,
+ kdb_getdr(i), i+1, kdb_getdr(i+1));
+
+ }
+ return 0;
+ }
+#if 0
+ case 'c':
+ {
+ unsigned long cr[5];
+
+ for (i=0; i<5; i++) {
+ cr[i] = kdb_getcr(i);
+ }
+ kdb_printf("cr0 = 0x%8.8x cr1 = 0x%8.8x cr2 = 0x%8.8x cr3 = 0x%8.8x\ncr4 = 0x%8.8x\n",
+ cr[0], cr[1], cr[2], cr[3], cr[4]);
+ return 0;
+ }
+#endif
+ case 'm':
+ break;
+ case 'r':
+ break;
+
+ case 's':
+ {
+ show_cur_stack_frame(regs, 0, NULL) ;
+
+ return 0 ;
+ }
+
+ case '%':
+ {
+ unsigned long contents ;
+
+ if (!kdbgetregcontents(type+1, regs, &contents))
+ kdb_printf("%s = 0x%16.16lx\n", type+1, contents) ;
+ else
+ kdb_printf("diag: Invalid register %s\n", type+1) ;
+
+ return 0 ;
+ }
+
+ default:
+ return KDB_BADREG;
+ }
+
+ /* NOTREACHED */
+ return 0;
+}
+
+k_machreg_t
+kdb_getpc(struct pt_regs *regs)
+{
+ return regs->cr_iip + ia64_psr(regs)->ri;
+}
+
+int
+kdb_setpc(struct pt_regs *regs, k_machreg_t newpc)
+{
+ regs->cr_iip = newpc & ~0xf;
+ ia64_psr(regs)->ri = newpc & 0x3;
+ return 0;
+}
+
+void
+kdb_disableint(kdbintstate_t *state)
+{
+ int *fp = (int *)state;
+ int flags;
+
+ __save_flags(flags);
+ __cli();
+
+ *fp = flags;
+}
+
+void
+kdb_restoreint(kdbintstate_t *state)
+{
+ int flags = *(int *)state;
+ __restore_flags(flags);
+}
+
+int
+kdb_putword(unsigned long addr, unsigned long contents)
+{
+ *(unsigned long *)addr = contents;
+ return 0;
+}
+
+int
+kdb_getcurrentframe(struct pt_regs *regs)
+{
+#if 0
+ regs->xcs = 0;
+#if defined(CONFIG_KDB_FRAMEPTR)
+ asm volatile("movl %%ebp,%0":"=m" (*(int *)®s->ebp));
+#endif
+ asm volatile("movl %%esp,%0":"=m" (*(int *)®s->esp));
+#endif
+ return 0;
+}
+
+unsigned long
+show_cur_stack_frame(struct pt_regs *regs, int regno, unsigned long *contents)
+{
+ long sof = regs->cr_ifs & ((1<<7)-1) ; /* size of frame */
+ unsigned long i ;
+ int j;
+ struct switch_stack *prs_regs = getprsregs(regs) ;
+ unsigned long *sofptr = (prs_regs? ia64_rse_skip_regs(
+ (unsigned long *)prs_regs->ar_bspstore, -sof) : NULL) ;
+
+ if (!sofptr) {
+ printk("Unable to display Current Stack Frame\n") ;
+ return 0 ;
+ }
+
+ if (regno < 0)
+ return 0 ;
+
+ for (i=sof, j=0;i;i--,j++) {
+ /* remember to skip the nat collection dword */
+ if ((((unsigned long)sofptr>>3) & (((1<<6)-1)))
+ == ((1<<6)-1))
+ sofptr++ ;
+
+ /* return the value in the reg if regno is non zero */
+
+ if (regno) {
+ if ((j+1) == regno) {
+ if (contents)
+ *contents = *sofptr ;
+ return -1;
+ }
+ sofptr++ ;
+ } else {
+ printk(" r%d: %016lx ", 32+j, *sofptr++) ;
+ if (!((j+1)%3)) printk("\n") ;
+ }
+ }
+
+ if (regno) {
+ if (!i) /* bogus rse number */
+ return 0 ;
+ } else
+ printk("\n") ;
+
+ return 0 ;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)