📄 xmon.c
字号:
/* * Routines providing a simple monitor for use on the PowerMac. * * Copyright (C) 1996 Paul Mackerras. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/config.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/smp.h>#include <linux/mm.h>#include <linux/reboot.h>#include <asm/ptrace.h>#include <asm/string.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/processor.h>#include <asm/pgtable.h>#include <asm/mmu.h>#include <asm/mmu_context.h>#include <asm/naca.h>#include <asm/paca.h>#include <asm/ppcdebug.h>#include "nonstdio.h"#include "privinst.h"#define scanhex xmon_scanhex#define skipbl xmon_skipbl#ifdef CONFIG_SMPstatic unsigned long cpus_in_xmon = 0;static unsigned long got_xmon = 0;static volatile int take_xmon = -1;#endif /* CONFIG_SMP */static unsigned long adrs;static int size = 1;static unsigned long ndump = 64;static unsigned long nidump = 16;static unsigned long ncsum = 4096;static int termch;static u_int bus_error_jmp[100];#define setjmp xmon_setjmp#define longjmp xmon_longjmp#define memlist_entry list_entry#define memlist_next(x) ((x)->next)#define memlist_prev(x) ((x)->prev)/* Max number of stack frames we are willing to produce on a backtrace. */#define MAXFRAMECOUNT 50/* Breakpoint stuff */struct bpt { unsigned long address; unsigned instr; unsigned long count; unsigned char enabled; char funcname[64]; /* function name for humans */};#define NBPTS 16static struct bpt bpts[NBPTS];static struct bpt dabr;static struct bpt iabr;static unsigned bpinstr = 0x7fe00008; /* trap *//* Prototypes */extern void (*debugger_fault_handler)(struct pt_regs *);static int cmds(struct pt_regs *);static int mread(unsigned long, void *, int);static int mwrite(unsigned long, void *, int);static void handle_fault(struct pt_regs *);static void byterev(unsigned char *, int);static void memex(void);static int bsesc(void);static void dump(void);static void prdump(unsigned long, long);#ifdef __MWERKS__static void prndump(unsigned, int);static int nvreadb(unsigned);#endifstatic int ppc_inst_dump(unsigned long, long);void print_address(unsigned long);static int getsp(void);static void dump_hash_table(void);static void backtrace(struct pt_regs *);static void excprint(struct pt_regs *);static void prregs(struct pt_regs *);static void memops(int);static void memlocate(void);static void memzcan(void);static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned);int skipbl(void);int scanhex(unsigned long *valp);static void scannl(void);static int hexdigit(int);void getstring(char *, int);static void flush_input(void);static int inchar(void);static void take_input(char *);/* static void openforth(void); */static unsigned long read_spr(int);static void write_spr(int, unsigned long);static void super_regs(void);static void print_sysmap(void);static void remove_bpts(void);static void insert_bpts(void);static struct bpt *at_breakpoint(unsigned long pc);static void bpt_cmds(void);static void cacheflush(void);#ifdef CONFIG_SMPstatic void cpu_cmd(void);#endif /* CONFIG_SMP */static void csum(void);static void mem_translate(void);static void mem_check(void);static void mem_find_real(void);static void mem_find_vsid(void);static void mem_check_full_group(void);static void mem_check_pagetable_vsids (void);static void mem_map_check_slab(void);static void mem_map_lock_pages(void);static void mem_map_check_hash(void);static void mem_check_dup_rpn (void);static void debug_trace(void);extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned long);extern void printf(const char *fmt, ...);extern void xmon_vfprintf(void *f, const char *fmt, va_list ap);extern int xmon_putc(int c, void *f);extern int putchar(int ch);extern int xmon_read_poll(void);extern int setjmp(u_int *);extern void longjmp(u_int *, int);extern unsigned long _ASR;pte_t *find_linux_pte(pgd_t *pgdir, unsigned long va); /* from htab.c */#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])static char *help_string = "\Commands:\n\ b show breakpoints\n\ bd set data breakpoint\n\ bi set instruction breakpoint\n\ bc clear breakpoint\n\ d dump bytes\n\ di dump instructions\n\ df dump float values\n\ dd dump double values\n\ e print exception information\n\ f flush cache\n\ h dump hash table\n\ m examine/change memory\n\ mm move a block of memory\n\ ms set a block of memory\n\ md compare two blocks of memory\n\ ml locate a block of memory\n\ mz zero a block of memory\n\ mx translation information for an effective address\n\ mi show information about memory allocation\n\ M print System.map\n\ p show the task list\n\ r print registers\n\ s single step\n\ S print special registers\n\ t print backtrace\n\ T Enable/Disable PPCDBG flags\n\ x exit monitor\n\ z reboot\n\ Z halt\n\";static int xmon_trace[NR_CPUS];#define SSTEP 1 /* stepping because of 's' command */#define BRSTEP 2 /* stepping over breakpoint *//* * Stuff for reading and writing memory safely */extern inline void sync(void){ asm volatile("sync; isync");}extern inline void __delay(unsigned int loops){ if (loops != 0) __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : "r" (loops) : "ctr");}/* (Ref: 64-bit PowerPC ELF ABI Spplement; Ian Lance Taylor, Zembu Labs). A PPC stack frame looks like this: High Address Back Chain FP reg save area GP reg save area Local var space Parameter save area (SP+48) TOC save area (SP+40) link editor doubleword (SP+32) compiler doubleword (SP+24) LR save (SP+16) CR save (SP+8) Back Chain (SP+0) Note that the LR (ret addr) may not be saved in the current frame if no functions have been called from the current function. *//* A traceback table typically follows each function. The find_tb_table() func will fill in this struct. Note that the struct is not an exact match with the encoded table defined by the ABI. It is defined here more for programming convenience. */struct tbtable { unsigned long flags; /* flags: */#define TBTAB_FLAGSGLOBALLINK (1L<<47)#define TBTAB_FLAGSISEPROL (1L<<46)#define TBTAB_FLAGSHASTBOFF (1L<<45)#define TBTAB_FLAGSINTPROC (1L<<44)#define TBTAB_FLAGSHASCTL (1L<<43)#define TBTAB_FLAGSTOCLESS (1L<<42)#define TBTAB_FLAGSFPPRESENT (1L<<41)#define TBTAB_FLAGSNAMEPRESENT (1L<<38)#define TBTAB_FLAGSUSESALLOCA (1L<<37)#define TBTAB_FLAGSSAVESCR (1L<<33)#define TBTAB_FLAGSSAVESLR (1L<<32)#define TBTAB_FLAGSSTORESBC (1L<<31)#define TBTAB_FLAGSFIXUP (1L<<30)#define TBTAB_FLAGSPARMSONSTK (1L<<0) unsigned char fp_saved; /* num fp regs saved f(32-n)..f31 */ unsigned char gpr_saved; /* num gpr's saved */ unsigned char fixedparms; /* num fixed point parms */ unsigned char floatparms; /* num float parms */ unsigned char parminfo[32]; /* types of args. null terminated */#define TBTAB_PARMFIXED 1#define TBTAB_PARMSFLOAT 2#define TBTAB_PARMDFLOAT 3 unsigned int tb_offset; /* offset from start of func */ unsigned long funcstart; /* addr of start of function */ char name[64]; /* name of function (null terminated)*/};static int find_tb_table(unsigned long codeaddr, struct tbtable *tab);voidxmon(struct pt_regs *excp){ struct pt_regs regs; int cmd; unsigned long msr; if (excp == NULL) { /* Ok, grab regs as they are now. This won't do a particularily good job because the prologue has already been executed. ToDo: We could reach back into the callers save area to do a better job of representing the caller's state. */ asm volatile ("std 0,0(%0)\n\ std 1,8(%0)\n\ std 2,16(%0)\n\ std 3,24(%0)\n\ std 4,32(%0)\n\ std 5,40(%0)\n\ std 6,48(%0)\n\ std 7,56(%0)\n\ std 8,64(%0)\n\ std 9,72(%0)\n\ std 10,80(%0)\n\ std 11,88(%0)\n\ std 12,96(%0)\n\ std 13,104(%0)\n\ std 14,112(%0)\n\ std 15,120(%0)\n\ std 16,128(%0)\n\ std 17,136(%0)\n\ std 18,144(%0)\n\ std 19,152(%0)\n\ std 20,160(%0)\n\ std 21,168(%0)\n\ std 22,176(%0)\n\ std 23,184(%0)\n\ std 24,192(%0)\n\ std 25,200(%0)\n\ std 26,208(%0)\n\ std 27,216(%0)\n\ std 28,224(%0)\n\ std 29,232(%0)\n\ std 30,240(%0)\n\ std 31,248(%0)" : : "b" (®s)); printf("xmon called\n"); /* Fetch the link reg for this stack frame. NOTE: the prev printf fills in the lr. */ regs.nip = regs.link = ((unsigned long *)(regs.gpr[1]))[2]; regs.msr = get_msr(); regs.ctr = get_ctr(); regs.xer = get_xer(); regs.ccr = get_cr(); regs.trap = 0; excp = ®s; } msr = get_msr(); set_msrd(msr & ~MSR_EE); /* disable interrupts */ excprint(excp);#ifdef CONFIG_SMP if (test_and_set_bit(smp_processor_id(), &cpus_in_xmon)) for (;;) ; while (test_and_set_bit(0, &got_xmon)) { if (take_xmon == smp_processor_id()) { take_xmon = -1; break; } } /* * XXX: breakpoints are removed while any cpu is in xmon */#endif /* CONFIG_SMP */ remove_bpts(); cmd = cmds(excp); if (cmd == 's') { xmon_trace[smp_processor_id()] = SSTEP; excp->msr |= 0x400; } else if (at_breakpoint(excp->nip)) { xmon_trace[smp_processor_id()] = BRSTEP; excp->msr |= 0x400; } else { xmon_trace[smp_processor_id()] = 0; insert_bpts(); }#ifdef CONFIG_SMP clear_bit(0, &got_xmon); clear_bit(smp_processor_id(), &cpus_in_xmon);#endif /* CONFIG_SMP */ set_msrd(msr); /* restore interrupt enable */}/* Code can call this to get a backtrace and continue. */voidxmon_backtrace(const char *fmt, ...){ va_list ap; struct pt_regs regs; /* Ok, grab regs as they are now. This won't do a particularily good job because the prologue has already been executed. ToDo: We could reach back into the callers save area to do a better job of representing the caller's state. */ asm volatile ("std 0,0(%0)\n\ std 1,8(%0)\n\ std 2,16(%0)\n\ std 3,24(%0)\n\ std 4,32(%0)\n\ std 5,40(%0)\n\ std 6,48(%0)\n\ std 7,56(%0)\n\ std 8,64(%0)\n\ std 9,72(%0)\n\ std 10,80(%0)\n\ std 11,88(%0)\n\ std 12,96(%0)\n\ std 13,104(%0)\n\ std 14,112(%0)\n\ std 15,120(%0)\n\ std 16,128(%0)\n\ std 17,136(%0)\n\ std 18,144(%0)\n\ std 19,152(%0)\n\ std 20,160(%0)\n\ std 21,168(%0)\n\ std 22,176(%0)\n\ std 23,184(%0)\n\ std 24,192(%0)\n\ std 25,200(%0)\n\ std 26,208(%0)\n\ std 27,216(%0)\n\ std 28,224(%0)\n\ std 29,232(%0)\n\ std 30,240(%0)\n\ std 31,248(%0)" : : "b" (®s)); /* Fetch the link reg for this stack frame. NOTE: the prev printf fills in the lr. */ regs.nip = regs.link = ((unsigned long *)(regs.gpr[1]))[2]; regs.msr = get_msr(); regs.ctr = get_ctr(); regs.xer = get_xer(); regs.ccr = get_cr(); regs.trap = 0; va_start(ap, fmt); xmon_vfprintf(stdout, fmt, ap); xmon_putc('\n', stdout); va_end(ap); take_input("\n"); backtrace(®s);}/* Call this to poll for ^C during busy operations. * Returns true if the user has hit ^C. */intxmon_interrupted(void){ int ret = xmon_read_poll(); if (ret == 3) { printf("\n^C interrupted.\n"); return 1; } return 0;}voidxmon_irq(int irq, void *d, struct pt_regs *regs){ unsigned long flags; __save_flags(flags); __cli(); printf("Keyboard interrupt\n"); xmon(regs); __restore_flags(flags);}intxmon_bpt(struct pt_regs *regs){ struct bpt *bp; bp = at_breakpoint(regs->nip); if (!bp) return 0; if (bp->count) { --bp->count; remove_bpts(); excprint(regs); xmon_trace[smp_processor_id()] = BRSTEP; regs->msr |= 0x400; } else { printf("Stopped at breakpoint %x (%lx %s)\n", (bp - bpts)+1, bp->address, bp->funcname); xmon(regs); } return 1;}intxmon_sstep(struct pt_regs *regs){ if (!xmon_trace[smp_processor_id()]) return 0; if (xmon_trace[smp_processor_id()] == BRSTEP) { xmon_trace[smp_processor_id()] = 0; insert_bpts(); } else { xmon(regs); } return 1;}intxmon_dabr_match(struct pt_regs *regs){ if (dabr.enabled && dabr.count) { --dabr.count; remove_bpts(); excprint(regs); xmon_trace[smp_processor_id()] = BRSTEP; regs->msr |= 0x400; } else { dabr.instr = regs->nip; xmon(regs); } return 1;}intxmon_iabr_match(struct pt_regs *regs){ if (iabr.enabled && iabr.count) { --iabr.count; remove_bpts(); excprint(regs); xmon_trace[smp_processor_id()] = BRSTEP; regs->msr |= 0x400; } else { xmon(regs); } return 1;}static struct bpt *at_breakpoint(unsigned long pc){ int i; struct bpt *bp; if (dabr.enabled && pc == dabr.instr) return &dabr; if (iabr.enabled && pc == iabr.address) return &iabr; bp = bpts; for (i = 0; i < NBPTS; ++i, ++bp) if (bp->enabled && pc == bp->address) return bp; return 0;}static voidinsert_bpts(){ int i; struct bpt *bp; if (naca->platform != PLATFORM_PSERIES) return; bp = bpts; for (i = 0; i < NBPTS; ++i, ++bp) { if (!bp->enabled) continue; if (mread(bp->address, &bp->instr, 4) != 4 || mwrite(bp->address, &bpinstr, 4) != 4) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -