/* $NetBSD: db_interface.c,v 1.112.4.1 2008/11/27 03:46:32 snj Exp $ */ /* * Copyright (c) 1996-2002 Eduardo Horvath. All rights reserved. * 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 <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.112.4.1 2008/11/27 03:46:32 snj Exp $"); #include "opt_ddb.h" #include "opt_multiprocessor.h" #include <sys/param.h> #include <sys/proc.h> #include <sys/user.h> #include <sys/reboot.h> #include <sys/systm.h> #include <sys/atomic.h> #include <uvm/uvm_extern.h> #include <dev/cons.h> #include <machine/db_machdep.h> #include <ddb/db_command.h> #include <ddb/db_sym.h> #include <ddb/db_variables.h> #include <ddb/db_extern.h> #include <ddb/db_access.h> #include <ddb/db_output.h> #include <ddb/db_interface.h> #include <ddb/ddbvar.h> #include <machine/instr.h> #include <machine/cpu.h> #include <machine/promlib.h> #include <machine/ctlreg.h> #include <machine/pmap.h> #include <machine/intr.h> #include "fb.h" extern struct traptrace { unsigned short tl:3, /* Trap level */ ns:4, /* PCB nsaved */ tt:9; /* Trap type */ unsigned short pid; /* PID */ u_int tstate; /* tstate */ u_int tsp; /* sp */ u_int tpc; /* pc */ u_int tfault; /* MMU tag access */ } trap_trace[], trap_trace_end[]; /* * Helpers for ddb variables. */ static uint64_t nil; #ifdef MULTIPROCESSOR #define pmap_ctx(PM) ((PM)->pm_ctx[cpu_number()]) #else #define pmap_ctx(PM) ((PM)->pm_ctx) #endif void fill_ddb_regs_from_tf(struct trapframe64 *tf); void ddb_restore_state(void); bool ddb_running_on_this_cpu(void); static int db_sparc_charop(const struct db_variable *vp, db_expr_t *val, int opcode) { char *regaddr = (char *)(((uint8_t *)DDB_REGS) + (size_t)vp->valuep); switch (opcode) { case DB_VAR_GET: *val = *regaddr; break; case DB_VAR_SET: *regaddr = *val; break; #ifdef DIAGNOSTIC default: printf("db_sparc_charop: opcode %d\n", opcode); break; #endif } return 0; } #ifdef not_used static int db_sparc_shortop(const struct db_variable *vp, db_expr_t *val, int opcode) { short *regaddr = (short *)(((uint8_t *)DDB_REGS) + (size_t)vp->valuep); switch (opcode) { case DB_VAR_GET: *val = *regaddr; break; case DB_VAR_SET: *regaddr = *val; break; #ifdef DIAGNOSTIC default: printf("sparc_shortop: opcode %d\n", opcode); break; #endif } return 0; } #endif static int db_sparc_intop(const struct db_variable *vp, db_expr_t *val, int opcode) { int *regaddr = (int *)(((uint8_t *)DDB_REGS) + (size_t)vp->valuep); switch (opcode) { case DB_VAR_GET: *val = *regaddr; break; case DB_VAR_SET: *regaddr = *val; break; #ifdef DIAGNOSTIC default: printf("db_sparc_intop: opcode %d\n", opcode); break; #endif } return 0; } static int db_sparc_regop(const struct db_variable *vp, db_expr_t *val, int opcode) { db_expr_t *regaddr = (db_expr_t *)(((uint8_t *)DDB_REGS) + (size_t)vp->valuep); switch (opcode) { case DB_VAR_GET: *val = *regaddr; break; case DB_VAR_SET: *regaddr = *val; break; #ifdef DIAGNOSTIC default: printf("db_sparc_regop: unknown op %d\n", opcode); break; #endif } return 0; } /* * Machine register set. */ #define dbreg(xx) (long *)offsetof(db_regs_t, db_tf.tf_ ## xx) #define dbregfr(xx) (long *)offsetof(db_regs_t, db_fr.fr_ ## xx) #define dbregfp(xx) (long *)offsetof(db_regs_t, db_fpstate.fs_ ## xx) static int db_sparc_regop(const struct db_variable *, db_expr_t *, int); const struct db_variable db_regs[] = { { "tstate", dbreg(tstate), db_sparc_regop, 0 }, { "pc", dbreg(pc), db_sparc_regop, 0 }, { "npc", dbreg(npc), db_sparc_regop, 0 }, { "ipl", dbreg(oldpil), db_sparc_charop, 0 }, { "y", dbreg(y), db_sparc_intop, 0 }, { "g0", (void *)&nil, FCN_NULL, 0 }, { "g1", dbreg(global[1]), db_sparc_regop, 0 }, { "g2", dbreg(global[2]), db_sparc_regop, 0 }, { "g3", dbreg(global[3]), db_sparc_regop, 0 }, { "g4", dbreg(global[4]), db_sparc_regop, 0 }, { "g5", dbreg(global[5]), db_sparc_regop, 0 }, { "g6", dbreg(global[6]), db_sparc_regop, 0 }, { "g7", dbreg(global[7]), db_sparc_regop, 0 }, { "o0", dbreg(out[0]), db_sparc_regop, 0 }, { "o1", dbreg(out[1]), db_sparc_regop, 0 }, { "o2", dbreg(out[2]), db_sparc_regop, 0 }, { "o3", dbreg(out[3]), db_sparc_regop, 0 }, { "o4", dbreg(out[4]), db_sparc_regop, 0 }, { "o5", dbreg(out[5]), db_sparc_regop, 0 }, { "o6", dbreg(out[6]), db_sparc_regop, 0 }, { "o7", dbreg(out[7]), db_sparc_regop, 0 }, { "l0", dbregfr(local[0]), db_sparc_regop, 0 }, { "l1", dbregfr(local[1]), db_sparc_regop, 0 }, { "l2", dbregfr(local[2]), db_sparc_regop, 0 }, { "l3", dbregfr(local[3]), db_sparc_regop, 0 }, { "l4", dbregfr(local[4]), db_sparc_regop, 0 }, { "l5", dbregfr(local[5]), db_sparc_regop, 0 }, { "l6", dbregfr(local[6]), db_sparc_regop, 0 }, { "l7", dbregfr(local[7]), db_sparc_regop, 0 }, { "i0", dbregfr(arg[0]), db_sparc_regop, 0 }, { "i1", dbregfr(arg[1]), db_sparc_regop, 0 }, { "i2", dbregfr(arg[2]), db_sparc_regop, 0 }, { "i3", dbregfr(arg[3]), db_sparc_regop, 0 }, { "i4", dbregfr(arg[4]), db_sparc_regop, 0 }, { "i5", dbregfr(arg[5]), db_sparc_regop, 0 }, { "i6", dbregfr(arg[6]), db_sparc_regop, 0 }, { "i7", dbregfr(arg[7]), db_sparc_regop, 0 }, { "f0", dbregfp(regs[0]), db_sparc_regop, 0 }, { "f2", dbregfp(regs[2]), db_sparc_regop, 0 }, { "f4", dbregfp(regs[4]), db_sparc_regop, 0 }, { "f6", dbregfp(regs[6]), db_sparc_regop, 0 }, { "f8", dbregfp(regs[8]), db_sparc_regop, 0 }, { "f10", dbregfp(regs[10]), db_sparc_regop, 0 }, { "f12", dbregfp(regs[12]), db_sparc_regop, 0 }, { "f14", dbregfp(regs[14]), db_sparc_regop, 0 }, { "f16", dbregfp(regs[16]), db_sparc_regop, 0 }, { "f18", dbregfp(regs[18]), db_sparc_regop, 0 }, { "f20", dbregfp(regs[20]), db_sparc_regop, 0 }, { "f22", dbregfp(regs[22]), db_sparc_regop, 0 }, { "f24", dbregfp(regs[24]), db_sparc_regop, 0 }, { "f26", dbregfp(regs[26]), db_sparc_regop, 0 }, { "f28", dbregfp(regs[28]), db_sparc_regop, 0 }, { "f30", dbregfp(regs[30]), db_sparc_regop, 0 }, { "f32", dbregfp(regs[32]), db_sparc_regop, 0 }, { "f34", dbregfp(regs[34]), db_sparc_regop, 0 }, { "f36", dbregfp(regs[36]), db_sparc_regop, 0 }, { "f38", dbregfp(regs[38]), db_sparc_regop, 0 }, { "f40", dbregfp(regs[40]), db_sparc_regop, 0 }, { "f42", dbregfp(regs[42]), db_sparc_regop, 0 }, { "f44", dbregfp(regs[44]), db_sparc_regop, 0 }, { "f46", dbregfp(regs[46]), db_sparc_regop, 0 }, { "f48", dbregfp(regs[48]), db_sparc_regop, 0 }, { "f50", dbregfp(regs[50]), db_sparc_regop, 0 }, { "f52", dbregfp(regs[52]), db_sparc_regop, 0 }, { "f54", dbregfp(regs[54]), db_sparc_regop, 0 }, { "f56", dbregfp(regs[56]), db_sparc_regop, 0 }, { "f58", dbregfp(regs[58]), db_sparc_regop, 0 }, { "f60", dbregfp(regs[60]), db_sparc_regop, 0 }, { "f62", dbregfp(regs[62]), db_sparc_regop, 0 }, { "fsr", dbregfp(fsr), db_sparc_regop, 0 }, { "gsr", dbregfp(gsr), db_sparc_intop, 0 }, }; const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); int db_active = 0; extern char *trap_type[]; void kdb_kbd_trap(struct trapframe64 *); void db_prom_cmd(db_expr_t, bool, db_expr_t, const char *); void db_lwp_cmd(db_expr_t, bool, db_expr_t, const char *); void db_proc_cmd(db_expr_t, bool, db_expr_t, const char *); void db_ctx_cmd(db_expr_t, bool, db_expr_t, const char *); void db_dump_pcb(db_expr_t, bool, db_expr_t, const char *); void db_dump_pv(db_expr_t, bool, db_expr_t, const char *); void db_setpcb(db_expr_t, bool, db_expr_t, const char *); void db_dump_dtlb(db_expr_t, bool, db_expr_t, const char *); void db_dump_itlb(db_expr_t, bool, db_expr_t, const char *); void db_dump_dtsb(db_expr_t, bool, db_expr_t, const char *); void db_dump_itsb(db_expr_t, bool, db_expr_t, const char *); void db_pmap_kernel(db_expr_t, bool, db_expr_t, const char *); void db_pload_cmd(db_expr_t, bool, db_expr_t, const char *); void db_pmap_cmd(db_expr_t, bool, db_expr_t, const char *); void db_traptrace(db_expr_t, bool, db_expr_t, const char *); void db_watch(db_expr_t, bool, db_expr_t, const char *); void db_pm_extract(db_expr_t, bool, db_expr_t, const char *); void db_cpu_cmd(db_expr_t, bool, db_expr_t, const char *); void db_sir_cmd(db_expr_t, bool, db_expr_t, const char *); #ifdef DDB static void db_dump_pmap(struct pmap *); static void db_print_trace_entry(struct traptrace *, int); #ifdef MULTIPROCESSOR #define NOCPU -1 static int db_suspend_others(void); static void ddb_suspend(struct trapframe64 *); void db_resume_others(void); int ddb_cpu = NOCPU; bool ddb_running_on_this_cpu(void) { return ddb_cpu == cpu_number(); } static int db_suspend_others(void) { int cpu_me = cpu_number(); bool win; if (cpus == NULL) return 1; win = atomic_cas_32(&ddb_cpu, NOCPU, cpu_me) == (uint32_t)NOCPU; if (win) mp_pause_cpus(); return win; } void db_resume_others(void) { int cpu_me = cpu_number(); if (atomic_cas_32(&ddb_cpu, cpu_me, NOCPU) == cpu_me) mp_resume_cpus(); } static void ddb_suspend(struct trapframe64 *tf) { sparc64_ipi_pause_thiscpu(tf); } #endif /* MULTIPROCESSOR */ /* * Received keyboard interrupt sequence. */ void kdb_kbd_trap(struct trapframe64 *tf) { if (db_active == 0 /* && (boothowto & RB_KDB) */) { printf("\n\nkernel: keyboard interrupt tf=%p\n", tf); kdb_trap(-1, tf); } } void fill_ddb_regs_from_tf(struct trapframe64 *tf) { extern int savetstate(struct trapstate *); #ifdef MULTIPROCESSOR static db_regs_t ddbregs[CPUSET_MAXNUMCPU]; curcpu()->ci_ddb_regs = &ddbregs[cpu_number()]; #else static db_regs_t ddbregs; curcpu()->ci_ddb_regs = &ddbregs; #endif DDB_REGS->db_tf = *tf; #ifdef __arch64__ DDB_REGS->db_fr = *(struct frame64 *)(uintptr_t)tf->tf_out[6]; #else { struct frame32 *tf32 = (struct frame32 *)(uintptr_t)tf->tf_out[6]; int i; for (i = 0; i < 8; i++) DDB_REGS->db_fr.fr_local[i] = (uint32_t)tf32->fr_local[i]; for (i = 0; i < 6; i++) DDB_REGS->db_fr.fr_arg[i] = (uint32_t)tf32->fr_arg[i]; DDB_REGS->db_fr.fr_fp = tf32->fr_fp; DDB_REGS->db_fr.fr_pc = tf32->fr_pc; } #endif if (fplwp) { savefpstate(fplwp->l_md.md_fpstate); DDB_REGS->db_fpstate = *fplwp->l_md.md_fpstate; loadfpstate(fplwp->l_md.md_fpstate); } /* We should do a proper copyin and xlate 64-bit stack frames, but... */ /* if (tf->tf_tstate & TSTATE_PRIV) { .. } */ #if 0 /* make sure this is not causing ddb problems. */ if (tf->tf_out[6] & 1) { if ((unsigned)(tf->tf_out[6] + BIAS) > (unsigned)KERNBASE) DDB_REGS->db_fr = *(struct frame64 *)(tf->tf_out[6] + BIAS); else copyin((void *)(tf->tf_out[6] + BIAS), &DDB_REGS->db_fr, sizeof(struct frame64)); } else { struct frame32 tfr; int i; /* First get a local copy of the frame32 */ if ((unsigned)(tf->tf_out[6]) > (unsigned)KERNBASE) tfr = *(struct frame32 *)tf->tf_out[6]; else copyin((void *)(tf->tf_out[6]), &tfr, sizeof(struct frame32)); /* Now copy each field from the 32-bit value to the 64-bit value */ for (i=0; i<8; i++) DDB_REGS->db_fr.fr_local[i] = tfr.fr_local[i]; for (i=0; i<6; i++) DDB_REGS->db_fr.fr_arg[i] = tfr.fr_arg[i]; DDB_REGS->db_fr.fr_fp = (long)tfr.fr_fp; DDB_REGS->db_fr.fr_pc = tfr.fr_pc; } #endif DDB_REGS->db_tl = savetstate(&DDB_REGS->db_ts[0]); } void ddb_restore_state() { extern void restoretstate(int, struct trapstate *); restoretstate(DDB_REGS->db_tl, &DDB_REGS->db_ts[0]); if (fplwp) { *fplwp->l_md.md_fpstate = DDB_REGS->db_fpstate; loadfpstate(fplwp->l_md.md_fpstate); } } /* * kdb_trap - field a TRACE or BPT trap */ int kdb_trap(int type, struct trapframe64 *tf) { int s; extern int trap_trace_dis; extern int doing_shutdown; trap_trace_dis++; doing_shutdown++; #if NFB > 0 fb_unblank(); #endif switch (type) { case T_BREAKPOINT: /* breakpoint */ break; case -1: /* keyboard interrupt */ printf("kdb tf=%p\n", tf); break; default: if (!db_onpanic && db_recover==0) return (0); printf("kernel trap %x: %s\n", type, trap_type[type & 0x1ff]); if (db_recover != 0) { prom_abort(); db_error("Faulted in DDB; continuing...\n"); prom_abort(); /*NOTREACHED*/ } db_recover = (label_t *)1; } /* Should switch to kdb`s own stack here. */ write_all_windows(); #if defined(MULTIPROCESSOR) if (!db_suspend_others()) { ddb_suspend(tf); return 1; } #endif /* Initialise local dbregs storage from trap frame */ fill_ddb_regs_from_tf(tf); s = splhigh(); db_active++; cnpollc(TRUE); /* Need to do spl stuff till cnpollc works */ db_dump_ts(0, 0, 0, 0); db_trap(type, 0/*code*/); ddb_restore_state(); cnpollc(FALSE); db_active--; splx(s); *tf = DDB_REGS->db_tf; curcpu()->ci_ddb_regs = NULL; trap_trace_dis--; doing_shutdown--; #if defined(MULTIPROCESSOR) db_resume_others(); #endif return (1); } #endif /* DDB */ /* * Read bytes from kernel address space for debugger. */ void db_read_bytes(db_addr_t addr, size_t size, char *data) { char *src; src = (char *)(uintptr_t)addr; while (size-- > 0) { if (src >= (char *)VM_MIN_KERNEL_ADDRESS) *data++ = probeget((paddr_t)(u_long)src++, ASI_P, 1); else *data++ = fubyte(src++); } } /* * Write bytes to kernel address space for debugger. */ void db_write_bytes(db_addr_t addr, size_t size, const char *data) { char *dst; extern paddr_t pmap_kextract(vaddr_t va); dst = (char *)(uintptr_t)addr; while (size-- > 0) { if ((dst >= (char *)VM_MIN_KERNEL_ADDRESS+0x400000)) *dst = *data; else if ((dst >= (char *)VM_MIN_KERNEL_ADDRESS) && (dst < (char *)VM_MIN_KERNEL_ADDRESS+0x400000)) /* Read Only mapping -- need to do a bypass access */ stba(pmap_kextract((vaddr_t)dst), ASI_PHYS_CACHED, *data); else subyte(dst, *data); dst++, data++; } } #ifdef DDB void Debugger() { /* We use the breakpoint to trap into DDB */ __asm("ta 1; nop"); } void db_prom_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { if (cpu_number()) { printf("this command is not safe while running on another " "CPU, please do\n\"mach cpu 0\" and then try again\n"); return; } prom_abort(); } void db_dump_dtlb(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { extern void print_dtlb(void); print_dtlb(); } void db_dump_itlb(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { extern void print_itlb(void); print_itlb(); } void db_pload_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { static paddr_t oldaddr = -1; int asi = ASI_PHYS_CACHED; if (!have_addr) { addr = oldaddr; } if (addr == -1) { db_printf("no address\n"); return; } addr &= ~0x7; /* align */ { register char c; register const char *cp = modif; while ((c = *cp++) != 0) if (c == 'u') asi = ASI_AIUS; } while (count--) { if (db_print_position() == 0) { /* Always print the address. */ db_printf("%16.16lx:\t", (long)addr); } oldaddr=addr; db_printf("%8.8lx\n", (long)ldxa(addr, asi)); addr += 8; if (db_print_position() != 0) db_end_line(); } } int64_t pseg_get(struct pmap *, vaddr_t); void db_dump_pmap(struct pmap *pm) { /* print all valid pages in the kernel pmap */ unsigned long long i, j, k, n, data0, data1; paddr_t *pdir, *ptbl; n = 0; for (i = 0; i < STSZ; i++) { pdir = (paddr_t *)(u_long)ldxa((vaddr_t)&pm->pm_segs[i], ASI_PHYS_CACHED); if (!pdir) { continue; } db_printf("pdir %lld at %lx:\n", i, (long)pdir); for (k = 0; k < PDSZ; k++) { ptbl = (paddr_t *)(u_long)ldxa((vaddr_t)&pdir[k], ASI_PHYS_CACHED); if (!ptbl) { continue; } db_printf("\tptable %lld:%lld at %lx:\n", i, k, (long)ptbl); for (j = 0; j < PTSZ; j++) { data0 = ldxa((vaddr_t)&ptbl[j], ASI_PHYS_CACHED); j++; data1 = ldxa((vaddr_t)&ptbl[j], ASI_PHYS_CACHED); if (!data0 && !data1) { continue; } db_printf("%016llx: %016llx\t", (i << STSHIFT) | (k << PDSHIFT) | ((j - 1) << PTSHIFT), data0); db_printf("%016llx: %016llx\n", (i << STSHIFT) | (k << PDSHIFT) | (j << PTSHIFT), data1); } } } } void db_pmap_kernel(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { extern struct pmap kernel_pmap_; int i, j, full = 0; uint64_t data; { register char c; register const char *cp = modif; while ((c = *cp++) != 0) if (c == 'f') full = 1; } if (have_addr) { /* lookup an entry for this VA */ if ((data = pseg_get(&kernel_pmap_, (vaddr_t)addr))) { db_printf("pmap_kernel(%p)->pm_segs[%lx][%lx][%lx]=>%qx\n", (void *)(uintptr_t)addr, (u_long)va_to_seg(addr), (u_long)va_to_dir(addr), (u_long)va_to_pte(addr), (unsigned long long)data); } else { db_printf("No mapping for %p\n", (void *)(uintptr_t)addr); } return; } db_printf("pmap_kernel(%p) psegs %p phys %llx\n", &kernel_pmap_, kernel_pmap_.pm_segs, (unsigned long long)kernel_pmap_.pm_physaddr); if (full) { db_dump_pmap(&kernel_pmap_); } else { for (j=i=0; i<STSZ; i++) { long seg = (long)ldxa((vaddr_t)&kernel_pmap_.pm_segs[i], ASI_PHYS_CACHED); if (seg) db_printf("seg %d => %lx%c", i, seg, (j++%4)?'\t':'\n'); } } } void db_pm_extract(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { if (have_addr) { paddr_t pa; if (pmap_extract(pmap_kernel(), addr, &pa)) db_printf("pa = %llx\n", (long long)pa); else db_printf("%p not found\n", (void *)(uintptr_t)addr); } else db_printf("pmap_extract: no address\n"); } void db_pmap_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { struct pmap* pm=NULL; int i, j=0, full = 0; { register char c; register const char *cp = modif; if (modif) while ((c = *cp++) != 0) if (c == 'f') full = 1; } if (curlwp && curlwp->l_proc->p_vmspace) pm = curlwp->l_proc->p_vmspace->vm_map.pmap; if (have_addr) pm = (struct pmap*)(uintptr_t)addr; db_printf("pmap %p: ctx %x refs %d physaddr %llx psegs %p\n", pm, pmap_ctx(pm), pm->pm_refs, (unsigned long long)pm->pm_physaddr, pm->pm_segs); if (full) { db_dump_pmap(pm); } else { for (i=0; i<STSZ; i++) { long seg = (long)ldxa((vaddr_t)&kernel_pmap_.pm_segs[i], ASI_PHYS_CACHED); if (seg) db_printf("seg %d => %lx%c", i, seg, (j++%4)?'\t':'\n'); } } } #define TSBENTS (512 << tsbsize) extern pte_t *tsb_dmmu, *tsb_immu; extern int tsbsize; void db_dump_tsb_common(pte_t *); void db_dump_dtsb(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { db_printf("DTSB:\n"); db_dump_tsb_common(curcpu()->ci_tsb_dmmu); } void db_dump_itsb(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { db_printf("ITSB:\n"); db_dump_tsb_common(curcpu()->ci_tsb_immu); } void db_dump_tsb_common(pte_t *tsb) { uint64_t tag, data; int i; for (i = 0; i < TSBENTS; i++) { tag = tsb[i].tag; data = tsb[i].data; db_printf("%4d:%4d:%08x %08x:%08x ", i, (int)((tag & TSB_TAG_G) ? -1 : TSB_TAG_CTX(tag)), (int)((i << 13) | TSB_TAG_VA(tag)), (int)(data >> 32), (int)data); i++; tag = tsb[i].tag; data = tsb[i].data; db_printf("%4d:%4d:%08x %08x:%08x\n", i, (int)((tag & TSB_TAG_G) ? -1 : TSB_TAG_CTX(tag)), (int)((i << 13) | TSB_TAG_VA(tag)), (int)(data >> 32), (int)data); } } void db_page_cmd(db_expr_t, bool, db_expr_t, const char *); void db_page_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { if (!have_addr) { db_printf("Need paddr for page\n"); return; } db_printf("pa %llx pg %p\n", (unsigned long long)addr, PHYS_TO_VM_PAGE(addr)); } void db_lwp_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { struct lwp *l; l = curlwp; if (have_addr) l = (struct lwp*)(uintptr_t)addr; if (l == NULL) { db_printf("no current lwp\n"); return; } db_printf("lwp %p: lid %d\n", l, l->l_lid); db_printf("wchan:%p pri:%d epri:%d tf:%p\n", l->l_wchan, l->l_priority, lwp_eprio(l), l->l_md.md_tf); db_printf("pcb: %p fpstate: %p\n", &l->l_addr->u_pcb, l->l_md.md_fpstate); return; } void db_proc_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { struct proc *p = NULL; if (curlwp) p = curlwp->l_proc; if (have_addr) p = (struct proc*)(uintptr_t)addr; if (p == NULL) { db_printf("no current process\n"); return; } db_printf("process %p:", p); db_printf("pid:%d vmspace:%p pmap:%p ctx:%x\n", p->p_pid, p->p_vmspace, p->p_vmspace->vm_map.pmap, pmap_ctx(p->p_vmspace->vm_map.pmap)); db_printf("maxsaddr:%p ssiz:%dpg or %llxB\n", p->p_vmspace->vm_maxsaddr, p->p_vmspace->vm_ssize, (unsigned long long)ctob(p->p_vmspace->vm_ssize)); db_printf("profile timer: %ld sec %ld usec\n", p->p_stats->p_timer[ITIMER_PROF].it_value.tv_sec, p->p_stats->p_timer[ITIMER_PROF].it_value.tv_usec); return; } void db_ctx_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { struct proc *p; struct lwp *l; /* XXX LOCKING XXX */ LIST_FOREACH(p, &allproc, p_list) { if (p->p_stat) { db_printf("process %p:", p); db_printf("pid:%d pmap:%p ctx:%x\n", p->p_pid, p->p_vmspace->vm_map.pmap, pmap_ctx(p->p_vmspace->vm_map.pmap)); LIST_FOREACH(l, &p->p_lwps, l_sibling) { db_printf("\tlwp %p: lid:%d tf:%p fpstate %p " "lastcall:%s\n", l, l->l_lid, l->l_md.md_tf, l->l_md.md_fpstate, (l->l_addr->u_pcb.lastcall)? l->l_addr->u_pcb.lastcall : "Null"); } } } return; } void db_dump_pcb(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { struct pcb *pcb; int i; pcb = curpcb; if (have_addr) pcb = (struct pcb*)(uintptr_t)addr; db_printf("pcb@%p sp:%p pc:%p cwp:%d pil:%d nsaved:%x onfault:%p\nlastcall:%s\nfull windows:\n", pcb, (void *)(long)pcb->pcb_sp, (void *)(long)pcb->pcb_pc, pcb->pcb_cwp, pcb->pcb_pil, pcb->pcb_nsaved, (void *)pcb->pcb_onfault, (pcb->lastcall)?pcb->lastcall:"Null"); for (i=0; i<pcb->pcb_nsaved; i++) { db_printf("win %d: at %llx local, in\n", i, (unsigned long long)pcb->pcb_rw[i+1].rw_in[6]); db_printf("%16llx %16llx %16llx %16llx\n", (unsigned long long)pcb->pcb_rw[i].rw_local[0], (unsigned long long)pcb->pcb_rw[i].rw_local[1], (unsigned long long)pcb->pcb_rw[i].rw_local[2], (unsigned long long)pcb->pcb_rw[i].rw_local[3]); db_printf("%16llx %16llx %16llx %16llx\n", (unsigned long long)pcb->pcb_rw[i].rw_local[4], (unsigned long long)pcb->pcb_rw[i].rw_local[5], (unsigned long long)pcb->pcb_rw[i].rw_local[6], (unsigned long long)pcb->pcb_rw[i].rw_local[7]); db_printf("%16llx %16llx %16llx %16llx\n", (unsigned long long)pcb->pcb_rw[i].rw_in[0], (unsigned long long)pcb->pcb_rw[i].rw_in[1], (unsigned long long)pcb->pcb_rw[i].rw_in[2], (unsigned long long)pcb->pcb_rw[i].rw_in[3]); db_printf("%16llx %16llx %16llx %16llx\n", (unsigned long long)pcb->pcb_rw[i].rw_in[4], (unsigned long long)pcb->pcb_rw[i].rw_in[5], (unsigned long long)pcb->pcb_rw[i].rw_in[6], (unsigned long long)pcb->pcb_rw[i].rw_in[7]); } } void db_setpcb(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { struct proc *p, *pp; int ctx; if (!have_addr) { db_printf("What PID do you want to map in?\n"); return; } LIST_FOREACH(p, &allproc, p_list) { pp = p->p_pptr; if (p->p_stat && p->p_pid == addr) { #if 0 /* XXX Do we need to do the following too?: */ extern struct pcb *cpcb; curlwp = p; cpcb = (struct pcb*)p->p_addr; #endif if (p->p_vmspace->vm_map.pmap == pmap_kernel()) { db_printf("PID %ld has a kernel context.\n", (long)addr); return; } ctx = pmap_ctx(p->p_vmspace->vm_map.pmap); if (ctx < 0) { ctx = -ctx; pmap_ctx(p->p_vmspace->vm_map.pmap) = ctx; } else if (ctx == 0) { pmap_activate_pmap(p->p_vmspace->vm_map.pmap); ctx = pmap_ctx(p->p_vmspace->vm_map.pmap); } if (ctx > 0) { switchtoctx(ctx); return; } db_printf("could not activate pmap for PID %ld.\n", (long)addr); return; } } db_printf("PID %ld not found.\n", (long)addr); } static void db_print_trace_entry(struct traptrace *te, int i) { db_printf("%d:%d p:%d tt:%x:%llx:%llx %llx:%llx ", i, (int)te->tl, (int)te->pid, (int)te->tt, (unsigned long long)te->tstate, (unsigned long long)te->tfault, (unsigned long long)te->tsp, (unsigned long long)te->tpc); db_printsym((u_long)te->tpc, DB_STGY_PROC, db_printf); db_printf(": "); if ((te->tpc && !(te->tpc&0x3)) && curlwp && (curproc->p_pid == te->pid)) { db_disasm((u_long)te->tpc, 0); } else db_printf("\n"); } void db_traptrace(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { int i, start = 0, full = 0, reverse = 0; struct traptrace *end; start = 0; end = &trap_trace_end[0]; { register char c; register const char *cp = modif; if (modif) while ((c = *cp++) != 0) { if (c == 'f') full = 1; if (c == 'r') reverse = 1; } } if (have_addr) { start = addr / (sizeof (struct traptrace)); if (&trap_trace[start] > &trap_trace_end[0]) { db_printf("Address out of range.\n"); return; } if (!full) end = &trap_trace[start+1]; } db_printf("#:tl p:pid tt:tt:tstate:tfault sp:pc\n"); if (reverse) { if (full && start) for (i=start; --i;) { db_print_trace_entry(&trap_trace[i], i); } i = (end - &trap_trace[0]); while(--i > start) { db_print_trace_entry(&trap_trace[i], i); } } else { for (i=start; &trap_trace[i] < end ; i++) { db_print_trace_entry(&trap_trace[i], i); } if (full && start) for (i=0; i < start ; i++) { db_print_trace_entry(&trap_trace[i], i); } } } /* * Use physical or virtul watchpoint registers -- ugh * * UltraSPARC I and II have both a virtual and physical * watchpoint register. They are controlled by the LSU * control register. */ void db_watch(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { int phys = 0; int read = 0; int width = 8; /* Default to 8 bytes */ int64_t mask = 0xff; #define WATCH_VR (1L<<22) #define WATCH_VW (1L<<21) #define WATCH_PR (1L<<24) #define WATCH_PW (1L<<23) #define WATCH_PM_SHIFT 33 #define WATCH_PM (((uint64_t)0xffffL)<<WATCH_PM_SHIFT) #define WATCH_VM_SHIFT 25 #define WATCH_VM (((uint64_t)0xffffL)<<WATCH_VM_SHIFT) { register char c; register const char *cp = modif; if (modif) while ((c = *cp++) != 0) switch (c) { case 'p': /* Physical watchpoint */ phys = 1; break; case 'r': /* Trap reads too */ read = 1; break; case 'b': width = 1; mask = 0x1 << (addr & 0x7); break; case 'h': width = 2; mask = 0x3 << (addr & 0x6); break; case 'l': width = 4; mask = 0x7 << (addr & 0x4); break; case 'L': width = 8; mask = 0xf; break; default: break; } } if (have_addr) { /* turn on the watchpoint */ int64_t tmp = ldxa(0, ASI_MCCR); if (phys) { tmp &= ~WATCH_PM; tmp |= WATCH_PW | (mask << WATCH_PM_SHIFT); if (read) tmp |= WATCH_PR; stxa(PHYSICAL_WATCHPOINT, ASI_DMMU, addr); db_printf("Setting physical watchpoint to %llx-%llx\n", (long long)addr, (long long)addr + width); } else { tmp &= ~WATCH_VM; tmp |= WATCH_VW | (mask << WATCH_VM_SHIFT); if (read) tmp |= WATCH_VR; stxa(VIRTUAL_WATCHPOINT, ASI_DMMU, addr); db_printf("Setting virtual watchpoint to %llx-%llx\n", (long long)addr, (long long)addr + width); } stxa(0, ASI_MCCR, tmp); } else { /* turn off the watchpoint */ int64_t tmp = ldxa(0, ASI_MCCR); if (phys) { tmp &= ~(WATCH_PM|WATCH_PR|WATCH_PW); db_printf("Disabling physical watchpoint\n"); } else { tmp &= ~(WATCH_VM|WATCH_VR|WATCH_VW); db_printf("Disabling virtual watchpoint\n"); } stxa(0, ASI_MCCR, tmp); } } /* XXX this belongs in cpu.c */ static void cpu_debug_dump(void); static void cpu_debug_dump(void) { struct cpu_info *ci; for (ci = cpus; ci; ci = ci->ci_next) { db_printf("cpu%d: self 0x%08lx lwp 0x%08lx pcb 0x%08lx\n", ci->ci_index, (u_long)ci->ci_self, (u_long)ci->ci_curlwp, (u_long)ci->ci_cpcb); } } void db_cpu_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { #ifdef MULTIPROCESSOR struct cpu_info *ci; #endif if (!have_addr) { cpu_debug_dump(); return; } #ifdef MULTIPROCESSOR for (ci = cpus; ci != NULL; ci = ci->ci_next) if (ci->ci_index == addr) break; if (ci == NULL) { db_printf("CPU %ld not configured\n", (long)addr); return; } if (ci != curcpu()) { if (!mp_cpu_is_paused(ci->ci_index)) { db_printf("CPU %ld not paused\n", (long)addr); return; } /* no locking needed - all other cpus are paused */ ddb_cpu = ci->ci_index; mp_resume_cpu(ddb_cpu); sparc64_do_pause(); } #endif } void db_sir_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) { __asm("sir; nop"); } const struct db_command db_machine_command_table[] = { { DDB_ADD_CMD("ctx", db_ctx_cmd, 0, "Print process MMU context information", NULL,NULL) }, #ifdef MULTIPROCESSOR { DDB_ADD_CMD("cpu", db_cpu_cmd, 0, "switch to another cpu", "cpu-no", NULL) }, #endif { DDB_ADD_CMD("dtlb", db_dump_dtlb, 0, "Print data translation look-aside buffer context information.", NULL,NULL) }, { DDB_ADD_CMD("itlb", db_dump_itlb, 0, "Display instruction translation storage buffer information.", NULL,NULL) }, { DDB_ADD_CMD("dtsb", db_dump_dtsb, 0, "Display data translation storage buffer information.", NULL,NULL) }, { DDB_ADD_CMD("itsb", db_dump_itsb, 0, "Display instruction translation storage buffer information.", NULL,NULL) }, { DDB_ADD_CMD("extract", db_pm_extract, 0, "Extract the physical address from the kernel pmap.", "address", " address:\tvirtual address to look up") }, { DDB_ADD_CMD("fpstate", db_dump_fpstate,0, "Dump the FPU state." ,NULL,NULL) }, { DDB_ADD_CMD("kmap", db_pmap_kernel, 0, "Display information about mappings in the kernel pmap.", "[/f] [address]", " address:\tdisplay the mapping for this virtual address\n" " /f:\tif no address is given, display a full dump of the pmap") }, { DDB_ADD_CMD("lwp", db_lwp_cmd, 0, "Display a struct lwp", "[address]", " address:\tthe struct lwp to print (curlwp otherwise)") }, { DDB_ADD_CMD("pcb", db_dump_pcb, 0, "Display information about a struct pcb", "[address]", " address:\tthe struct pcb to print (curpcb otherwise)") }, { DDB_ADD_CMD("pctx", db_setpcb, 0, "Attempt to change MMU process context","pid", " pid:\tthe process id to switch the MMU context to") }, { DDB_ADD_CMD("page", db_page_cmd, 0, "Display the address of a struct vm_page given a physical address", "pa", " pa:\tphysical address to look up") }, { DDB_ADD_CMD("phys", db_pload_cmd, 0, "Display physical memory.", "[address][,count]", " adddress:\tphysical address to start (8 byte aligned)\n" " count:\tnumber of bytes to display") }, { DDB_ADD_CMD("pmap", db_pmap_cmd, 0, "Display the pmap", "[/f] [pm_addr]", " pm_addr:\tAddress of struct pmap to display\n" " /f:\tdo a full dump of the pmap") }, { DDB_ADD_CMD("proc", db_proc_cmd, 0, "Display some information about a process", "[addr]"," addr:\tstruct proc address (curproc otherwise)") }, { DDB_ADD_CMD("prom", db_prom_cmd, 0, "Enter the OFW PROM.", NULL,NULL) }, { DDB_ADD_CMD("pv", db_dump_pv, 0, "Display a struct pv for a physical address", "pa", " pa:\tphysical address of a managed page") }, { DDB_ADD_CMD("sir", db_sir_cmd, 0, "do a Software Initiated Reset (entering PROM)", NULL,NULL) }, { DDB_ADD_CMD("stack", db_dump_stack, 0, "Dump the window stack.", "[/u] [addr]", " addr:\tstart address of dump (current stack otherwise)\n" " /u:\tcontinue trace into userland") }, { DDB_ADD_CMD("tf", db_dump_trap, 0, "Display full trap frame state.", "[/u] [addr]", " addr:\tdisplay this trap frame (current kernel frame otherwise)\n" " /u:\tdisplay the current userland trap frame") }, { DDB_ADD_CMD("ts", db_dump_ts, 0, "Display trap state.", NULL,NULL) }, { DDB_ADD_CMD("traptrace", db_traptrace, 0, "Display or set trap trace information.", "[/fr] [addr]", " addr:\tstart address of trace\n" " /f:\tdisplay full information\n" " /r:\treverse the trace order") }, { DDB_ADD_CMD("watch", db_watch, 0, "Set or clear a physical or virtual hardware watchpoint.", "[/prbhlL] [addr]", " addr:\tset the breakpoint (clear watchpoint if not present)\n" " /p:\taddress is physical\n" " /r:\ttrap on reads too (otherwise only write access)\n" " /b:\t8 bit\n" " /h:\t16 bit\n" " /l:\t32 bit\n" " /L:\t64 bit") }, { DDB_ADD_CMD("window", db_dump_window, 0, "Print register window information", "[no]", " no:\tstack frame number (0, i.e. top, if missing)") }, { DDB_ADD_CMD(NULL, NULL, 0, NULL,NULL,NULL) } }; #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(int inst, db_addr_t pc, db_regs_t *regs) { union instr insn; db_addr_t npc; 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"); } } bool db_inst_branch(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; } } bool db_inst_call(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; } } bool db_inst_unconditional_flow_transfer(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; } } bool db_inst_return(int inst) { return (inst == I_JMPLri(I_G0, I_O7, 8) || /* ret */ inst == I_JMPLri(I_G0, I_I7, 8)); /* retl */ } bool db_inst_trap_return(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(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(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; } }