📄 xmon.c
字号:
/* * Routines providing a simple monitor for use on the PowerMac. * * Copyright (C) 1996-2005 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 <linux/delay.h>#include <linux/kallsyms.h>#include <linux/cpumask.h>#include <linux/module.h>#include <linux/sysrq.h>#include <linux/interrupt.h>#include <asm/ptrace.h>#include <asm/string.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/xmon.h>#ifdef CONFIG_PMAC_BACKLIGHT#include <asm/backlight.h>#endif#include <asm/processor.h>#include <asm/pgtable.h>#include <asm/mmu.h>#include <asm/mmu_context.h>#include <asm/cputable.h>#include <asm/rtas.h>#include <asm/sstep.h>#include <asm/bug.h>#ifdef CONFIG_PPC64#include <asm/hvcall.h>#include <asm/paca.h>#endif#include "nonstdio.h"#define scanhex xmon_scanhex#define skipbl xmon_skipbl#ifdef CONFIG_SMPcpumask_t cpus_in_xmon = CPU_MASK_NONE;static unsigned long xmon_taken = 1;static int xmon_owner;static int xmon_gate;#endif /* CONFIG_SMP */static unsigned long in_xmon = 0;static unsigned long adrs;static int size = 1;#define MAX_DUMP (128 * 1024)static unsigned long ndump = 64;static unsigned long nidump = 16;static unsigned long ncsum = 4096;static int termch;static char tmpstr[128];#define JMP_BUF_LEN 23static long bus_error_jmp[JMP_BUF_LEN];static int catch_memory_errors;static long *xmon_fault_jmp[NR_CPUS];#define setjmp xmon_setjmp#define longjmp xmon_longjmp/* Breakpoint stuff */struct bpt { unsigned long address; unsigned int instr[2]; atomic_t ref_count; int enabled; unsigned long pad;};/* Bits in bpt.enabled */#define BP_IABR_TE 1 /* IABR translation enabled */#define BP_IABR 2#define BP_TRAP 8#define BP_DABR 0x10#define NBPTS 256static struct bpt bpts[NBPTS];static struct bpt dabr;static struct bpt *iabr;static unsigned bpinstr = 0x7fe00008; /* trap */#define BP_NUM(bp) ((bp) - bpts + 1)/* Prototypes */static int cmds(struct pt_regs *);static int mread(unsigned long, void *, int);static int mwrite(unsigned long, void *, int);static int 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);static int ppc_inst_dump(unsigned long, long, int);void print_address(unsigned long);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 unsigned long read_spr(int);static void write_spr(int, unsigned long);static void super_regs(void);static void remove_bpts(void);static void insert_bpts(void);static void remove_cpu_bpts(void);static void insert_cpu_bpts(void);static struct bpt *at_breakpoint(unsigned long pc);static struct bpt *in_breakpoint_table(unsigned long pc, unsigned long *offp);static int do_step(struct pt_regs *);static void bpt_cmds(void);static void cacheflush(void);static int cpu_cmd(void);static void csum(void);static void bootcmds(void);static void proccall(void);void dump_segments(void);static void symbol_lookup(void);static void xmon_print_symbol(unsigned long address, const char *mid, const char *after);static const char *getvecname(unsigned long vec);extern int print_insn_powerpc(unsigned long, unsigned long, int);extern void xmon_enter(void);extern void xmon_leave(void);extern long setjmp(long *);extern void longjmp(long *, long);extern void xmon_save_regs(struct pt_regs *);#ifdef CONFIG_PPC64#define REG "%.16lx"#define REGS_PER_LINE 4#define LAST_VOLATILE 13#else#define REG "%.8lx"#define REGS_PER_LINE 8#define LAST_VOLATILE 12#endif#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ || ('a' <= (c) && (c) <= 'f') \ || ('A' <= (c) && (c) <= 'F'))#define isalnum(c) (('0' <= (c) && (c) <= '9') \ || ('a' <= (c) && (c) <= 'z') \ || ('A' <= (c) && (c) <= 'Z'))#define isspace(c) (c == ' ' || c == '\t' || c == 10 || c == 13 || c == 0)static char *help_string = "\Commands:\n\ b show breakpoints\n\ bd set data breakpoint\n\ bi set instruction breakpoint\n\ bc clear breakpoint\n"#ifdef CONFIG_SMP "\ c print cpus stopped in xmon\n\ c# try to switch to cpu number h (in hex)\n"#endif "\ C checksum\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\ la lookup symbol+offset of specified address\n\ ls lookup address of specified symbol\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\ mi show information about memory allocation\n\ p call a procedure\n\ r print registers\n\ s single step\n\ S print special registers\n\ t print backtrace\n\ x exit monitor and recover\n\ X exit monitor and dont recover\n"#ifdef CONFIG_PPC64" u dump segment table or SLB\n"#endif#ifdef CONFIG_PPC_STD_MMU_32" u dump segment registers\n"#endif" ? help\n"" zr reboot\n\ zh halt\n";static struct pt_regs *xmon_regs;static inline void sync(void){ asm volatile("sync; isync");}static inline void store_inst(void *p){ asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p));}static inline void cflush(void *p){ asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p));}static inline void cinval(void *p){ asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p));}/* * Disable surveillance (the service processor watchdog function) * while we are in xmon. * XXX we should re-enable it when we leave. :) */#define SURVEILLANCE_TOKEN 9000static inline void disable_surveillance(void){#ifdef CONFIG_PPC_PSERIES /* Since this can't be a module, args should end up below 4GB. */ static struct rtas_args args; /* * At this point we have got all the cpus we can into * xmon, so there is hopefully no other cpu calling RTAS * at the moment, even though we don't take rtas.lock. * If we did try to take rtas.lock there would be a * real possibility of deadlock. */ args.token = rtas_token("set-indicator"); if (args.token == RTAS_UNKNOWN_SERVICE) return; args.nargs = 3; args.nret = 1; args.rets = &args.args[3]; args.args[0] = SURVEILLANCE_TOKEN; args.args[1] = 0; args.args[2] = 0; enter_rtas(__pa(&args));#endif /* CONFIG_PPC_PSERIES */}#ifdef CONFIG_SMPstatic int xmon_speaker;static void get_output_lock(void){ int me = smp_processor_id() + 0x100; int last_speaker = 0, prev; long timeout; if (xmon_speaker == me) return; for (;;) { if (xmon_speaker == 0) { last_speaker = cmpxchg(&xmon_speaker, 0, me); if (last_speaker == 0) return; } timeout = 10000000; while (xmon_speaker == last_speaker) { if (--timeout > 0) continue; /* hostile takeover */ prev = cmpxchg(&xmon_speaker, last_speaker, me); if (prev == last_speaker) return; break; } }}static void release_output_lock(void){ xmon_speaker = 0;}#endifint xmon_core(struct pt_regs *regs, int fromipi){ int cmd = 0; unsigned long msr; struct bpt *bp; long recurse_jmp[JMP_BUF_LEN]; unsigned long offset;#ifdef CONFIG_SMP int cpu; int secondary; unsigned long timeout;#endif msr = mfmsr(); mtmsr(msr & ~MSR_EE); /* disable interrupts */ bp = in_breakpoint_table(regs->nip, &offset); if (bp != NULL) { regs->nip = bp->address + offset; atomic_dec(&bp->ref_count); } remove_cpu_bpts();#ifdef CONFIG_SMP cpu = smp_processor_id(); if (cpu_isset(cpu, cpus_in_xmon)) { get_output_lock(); excprint(regs); printf("cpu 0x%x: Exception %lx %s in xmon, " "returning to main loop\n", cpu, regs->trap, getvecname(TRAP(regs))); release_output_lock(); longjmp(xmon_fault_jmp[cpu], 1); } if (setjmp(recurse_jmp) != 0) { if (!in_xmon || !xmon_gate) { get_output_lock(); printf("xmon: WARNING: bad recursive fault " "on cpu 0x%x\n", cpu); release_output_lock(); goto waiting; } secondary = !(xmon_taken && cpu == xmon_owner); goto cmdloop; } xmon_fault_jmp[cpu] = recurse_jmp; cpu_set(cpu, cpus_in_xmon); bp = NULL; if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) bp = at_breakpoint(regs->nip); if (bp || (regs->msr & MSR_RI) == 0) fromipi = 0; if (!fromipi) { get_output_lock(); excprint(regs); if (bp) { printf("cpu 0x%x stopped at breakpoint 0x%x (", cpu, BP_NUM(bp)); xmon_print_symbol(regs->nip, " ", ")\n"); } if ((regs->msr & MSR_RI) == 0) printf("WARNING: exception is not recoverable, " "can't continue\n"); release_output_lock(); } waiting: secondary = 1; while (secondary && !xmon_gate) { if (in_xmon == 0) { if (fromipi) goto leave; secondary = test_and_set_bit(0, &in_xmon); } barrier(); } if (!secondary && !xmon_gate) { /* we are the first cpu to come in */ /* interrupt other cpu(s) */ int ncpus = num_online_cpus(); xmon_owner = cpu; mb(); if (ncpus > 1) { smp_send_debugger_break(MSG_ALL_BUT_SELF); /* wait for other cpus to come in */ for (timeout = 100000000; timeout != 0; --timeout) { if (cpus_weight(cpus_in_xmon) >= ncpus) break; barrier(); } } remove_bpts(); disable_surveillance(); /* for breakpoint or single step, print the current instr. */ if (bp || TRAP(regs) == 0xd00) ppc_inst_dump(regs->nip, 1, 0); printf("enter ? for help\n"); mb(); xmon_gate = 1; barrier(); } cmdloop: while (in_xmon) { if (secondary) { if (cpu == xmon_owner) { if (!test_and_set_bit(0, &xmon_taken)) { secondary = 0; continue; } /* missed it */ while (cpu == xmon_owner) barrier(); } barrier(); } else { cmd = cmds(regs); if (cmd != 0) { /* exiting xmon */ insert_bpts(); xmon_gate = 0; wmb(); in_xmon = 0; break; } /* have switched to some other cpu */ secondary = 1; } } leave: cpu_clear(cpu, cpus_in_xmon); xmon_fault_jmp[cpu] = NULL;#else /* UP is simple... */ if (in_xmon) { printf("Exception %lx %s in xmon, returning to main loop\n", regs->trap, getvecname(TRAP(regs))); longjmp(xmon_fault_jmp[0], 1); } if (setjmp(recurse_jmp) == 0) { xmon_fault_jmp[0] = recurse_jmp; in_xmon = 1; excprint(regs); bp = at_breakpoint(regs->nip); if (bp) { printf("Stopped at breakpoint %x (", BP_NUM(bp)); xmon_print_symbol(regs->nip, " ", ")\n"); } if ((regs->msr & MSR_RI) == 0) printf("WARNING: exception is not recoverable, " "can't continue\n"); remove_bpts(); disable_surveillance(); /* for breakpoint or single step, print the current instr. */ if (bp || TRAP(regs) == 0xd00) ppc_inst_dump(regs->nip, 1, 0); printf("enter ? for help\n"); } cmd = cmds(regs); insert_bpts(); in_xmon = 0;#endif if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) { bp = at_breakpoint(regs->nip); if (bp != NULL) { int stepped = emulate_step(regs, bp->instr[0]); if (stepped == 0) { regs->nip = (unsigned long) &bp->instr[0]; atomic_inc(&bp->ref_count); } else if (stepped < 0) { printf("Couldn't single-step %s instruction\n", (IS_RFID(bp->instr[0])? "rfid": "mtmsrd")); } } } insert_cpu_bpts(); mtmsr(msr); /* restore interrupt enable */ return cmd != 'X';}int xmon(struct pt_regs *excp){ struct pt_regs regs; if (excp == NULL) { xmon_save_regs(®s); excp = ®s; } return xmon_core(excp, 0);}EXPORT_SYMBOL(xmon);irqreturn_txmon_irq(int irq, void *d, struct pt_regs *regs){ unsigned long flags; local_irq_save(flags); printf("Keyboard interrupt\n"); xmon(regs); local_irq_restore(flags); return IRQ_HANDLED;}int xmon_bpt(struct pt_regs *regs){ struct bpt *bp; unsigned long offset; if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF)) return 0; /* Are we at the trap at bp->instr[1] for some bp? */ bp = in_breakpoint_table(regs->nip, &offset); if (bp != NULL && offset == 4) { regs->nip = bp->address + 4; atomic_dec(&bp->ref_count); return 1; } /* Are we at a breakpoint? */ bp = at_breakpoint(regs->nip); if (!bp) return 0; xmon_core(regs, 0); return 1;}int xmon_sstep(struct pt_regs *regs){ if (user_mode(regs)) return 0; xmon_core(regs, 0); return 1;}int xmon_dabr_match(struct pt_regs *regs){ if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF)) return 0; if (dabr.enabled == 0) return 0; xmon_core(regs, 0); return 1;}int xmon_iabr_match(struct pt_regs *regs){ if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF)) return 0; if (iabr == 0) return 0; xmon_core(regs, 0); return 1;}int xmon_ipi(struct pt_regs *regs){#ifdef CONFIG_SMP if (in_xmon && !cpu_isset(smp_processor_id(), cpus_in_xmon)) xmon_core(regs, 1);#endif return 0;}int xmon_fault_handler(struct pt_regs *regs){ struct bpt *bp; unsigned long offset; if (in_xmon && catch_memory_errors) handle_fault(regs); /* doesn't return */ if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) { bp = in_breakpoint_table(regs->nip, &offset); if (bp != NULL) { regs->nip = bp->address + offset; atomic_dec(&bp->ref_count); } } return 0;}static struct bpt *at_breakpoint(unsigned long pc){ int i; struct bpt *bp; bp = bpts; for (i = 0; i < NBPTS; ++i, ++bp) if (bp->enabled && pc == bp->address) return bp; return NULL;}static struct bpt *in_breakpoint_table(unsigned long nip, unsigned long *offp){ unsigned long off; off = nip - (unsigned long) bpts; if (off >= sizeof(bpts)) return NULL; off %= sizeof(struct bpt); if (off != offsetof(struct bpt, instr[0]) && off != offsetof(struct bpt, instr[1])) return NULL; *offp = off - offsetof(struct bpt, instr[0]); return (struct bpt *) (nip - off);}static struct bpt *new_breakpoint(unsigned long a)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -