diff options
author | Paul Mackerras <paulus@samba.org> | 2005-10-28 22:53:37 +1000 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2005-10-28 22:53:37 +1000 |
commit | f78541dcec327b0c46b150ee7d727f3db80275c4 (patch) | |
tree | 9336801742d93ffa0c84c76e2b3cdc5b50c7125b /arch/powerpc/xmon/xmon.c | |
parent | c032524f0ddea5fcc3a2cece0d4a61f37e5ca9cd (diff) |
powerpc: Merge xmon
The merged version follows the ppc64 version pretty closely mostly,
and in fact ARCH=ppc64 now uses the arch/powerpc/xmon version.
The main difference for ppc64 is that the 'p' command to call
show_state (which was always pretty dodgy) has been replaced by
the ppc32 'p' command, which calls a given procedure (so in fact
the old 'p' command behaviour can be achieved with 'p $show_state').
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/xmon/xmon.c')
-rw-r--r-- | arch/powerpc/xmon/xmon.c | 2530 |
1 files changed, 2530 insertions, 0 deletions
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c new file mode 100644 index 00000000000..d0623e0fd8e --- /dev/null +++ b/arch/powerpc/xmon/xmon.c @@ -0,0 +1,2530 @@ +/* + * 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 <linux/delay.h> +#include <linux/kallsyms.h> +#include <linux/cpumask.h> +#include <linux/module.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_SMP +cpumask_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 23 +static 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 256 +static 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 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 void xmon_enter(void); +extern void xmon_leave(void); + +extern int xmon_read_poll(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\ + T Enable/Disable PPCDBG flags\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 9000 + +static 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_SMP +static 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; +} +#endif + +int 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_t +xmon_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) +{ + struct bpt *bp; + + a &= ~3UL; + bp = at_breakpoint(a); + if (bp) + return bp; + + for (bp = bpts; bp < &bpts[NBPTS]; ++bp) { + if (!bp->enabled && atomic_read(&bp->ref_count) == 0) { + bp->address = a; + bp->instr[1] = bpinstr; + store_inst(&bp->instr[1]); + return bp; + } + } + + printf("Sorry, no free breakpoints. Please clear one first.\n"); + return NULL; +} + +static void insert_bpts(void) +{ + int i; + struct bpt *bp; + + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if ((bp->enabled & (BP_TRAP|BP_IABR)) == 0) + continue; + if (mread(bp->address, &bp->instr[0], 4) != 4) { + printf("Couldn't read instruction at %lx, " + "disabling breakpoint there\n", bp->address); + bp->enabled = 0; + continue; + } + if (IS_MTMSRD(bp->instr[0]) || IS_RFID(bp->instr[0])) { + printf("Breakpoint at %lx is on an mtmsrd or rfid " + "instruction, disabling it\n", bp->address); + bp->enabled = 0; + continue; + } + store_inst(&bp->instr[0]); + if (bp->enabled & BP_IABR) + continue; + if (mwrite(bp->address, &bpinstr, 4) != 4) { + printf("Couldn't write instruction at %lx, " + "disabling breakpoint there\n", bp->address); + bp->enabled &= ~BP_TRAP; + continue; + } + store_inst((void *)bp->address); + } +} + +static void insert_cpu_bpts(void) +{ + if (dabr.enabled) + set_dabr(dabr.address | (dabr.enabled & 7)); + if (iabr && cpu_has_feature(CPU_FTR_IABR)) + mtspr(SPRN_IABR, iabr->address + | (iabr->enabled & (BP_IABR|BP_IABR_TE))); +} + +static void remove_bpts(void) +{ + int i; + struct bpt *bp; + unsigned instr; + + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if ((bp->enabled & (BP_TRAP|BP_IABR)) != BP_TRAP) + continue; + if (mread(bp->address, &instr, 4) == 4 + && instr == bpinstr + && mwrite(bp->address, &bp->instr, 4) != 4) + printf("Couldn't remove breakpoint at %lx\n", + bp->address); + else + store_inst((void *)bp->address); + } +} + +static void remove_cpu_bpts(void) +{ + set_dabr(0); + if (cpu_has_feature(CPU_FTR_IABR)) + mtspr(SPRN_IABR, 0); +} + +/* Command interpreting routine */ +static char *last_cmd; + +static int +cmds(struct pt_regs *excp) +{ + int cmd = 0; + + last_cmd = NULL; + xmon_regs = excp; + for(;;) { +#ifdef CONFIG_SMP + printf("%x:", smp_processor_id()); +#endif /* CONFIG_SMP */ + printf("mon> "); + fflush(stdout); + flush_input(); + termch = 0; + cmd = skipbl(); + if( cmd == '\n' ) { + if (last_cmd == NULL) + continue; + take_input(last_cmd); + last_cmd = NULL; + cmd = inchar(); + } + switch (cmd) { + case 'm': + cmd = inchar(); + switch (cmd) { + case 'm': + case 's': + case 'd': + memops(cmd); + break; + case 'l': + memlocate(); + break; + case 'z': + memzcan(); + break; + case 'i': + show_mem(); + break; + default: + termch = cmd; + memex(); + } + break; + case 'd': + dump(); + break; + case 'l': + symbol_lookup(); + break; + case 'r': + prregs(excp); /* print regs */ + break; + case 'e': + excprint(excp); + break; + case 'S': + super_regs(); + break; + case 't': + backtrace(excp); + break; + case 'f': + cacheflush(); + break; + case 's': + if (do_step(excp)) + return cmd; + break; + case 'x': + case 'X': + case EOF: + return cmd; + case '?': + printf(help_string); + break; + case 'b': + bpt_cmds(); + break; + case 'C': + csum(); + break; + case 'c': + if (cpu_cmd()) + return 0; + break; + case 'z': + bootcmds(); + break; + case 'p': + proccall(); + break; +#ifdef CONFIG_PPC_STD_MMU + case 'u': + dump_segments(); + break; +#endif + default: + printf("Unrecognized command: "); + do { + if (' ' < cmd && cmd <= '~') + putchar(cmd); + else + printf("\\x%x", cmd); + cmd = inchar(); + } while (cmd != '\n'); + printf(" (type ? for help)\n"); + break; + } + } +} + +/* + * Step a single instruction. + * Some instructions we emulate, others we execute with MSR_SE set. + */ +static int do_step(struct pt_regs *regs) +{ + unsigned int instr; + int stepped; + + /* check we are in 64-bit kernel mode, translation enabled */ + if ((regs->msr & (MSR_SF|MSR_PR|MSR_IR)) == (MSR_SF|MSR_IR)) { + if (mread(regs->nip, &instr, 4) == 4) { + stepped = emulate_step(regs, instr); + if (stepped < 0) { + printf("Couldn't single-step %s instruction\n", + (IS_RFID(instr)? "rfid": "mtmsrd")); + return 0; + } + if (stepped > 0) { + regs->trap = 0xd00 | (regs->trap & 1); + printf("stepped to "); + xmon_print_symbol(regs->nip, " ", "\n"); + ppc_inst_dump(regs->nip, 1, 0); + return 0; + } + } + } + regs->msr |= MSR_SE; + return 1; +} + +static void bootcmds(void) +{ + int cmd; + + cmd = inchar(); + if (cmd == 'r') + ppc_md.restart(NULL); + else if (cmd == 'h') + ppc_md.halt(); + else if (cmd == 'p') + ppc_md.power_off(); +} + +static int cpu_cmd(void) +{ +#ifdef CONFIG_SMP + unsigned long cpu; + int timeout; + int count; + + if (!scanhex(&cpu)) { + /* print cpus waiting or in xmon */ + printf("cpus stopped:"); + count = 0; + for (cpu = 0; cpu < NR_CPUS; ++cpu) { + if (cpu_isset(cpu, cpus_in_xmon)) { + if (count == 0) + printf(" %x", cpu); + ++count; + } else { + if (count > 1) + printf("-%x", cpu - 1); + count = 0; + } + } + if (count > 1) + printf("-%x", NR_CPUS - 1); + printf("\n"); + return 0; + } + /* try to switch to cpu specified */ + if (!cpu_isset(cpu, cpus_in_xmon)) { + printf("cpu 0x%x isn't in xmon\n", cpu); + return 0; + } + xmon_taken = 0; + mb(); + xmon_owner = cpu; + timeout = 10000000; + while (!xmon_taken) { + if (--timeout == 0) { + if (test_and_set_bit(0, &xmon_taken)) + break; + /* take control back */ + mb(); + xmon_owner = smp_processor_id(); + printf("cpu %u didn't take control\n", cpu); + return 0; + } + barrier(); + } + return 1; +#else + return 0; +#endif /* CONFIG_SMP */ +} + +static unsigned short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +static void +csum(void) +{ + unsigned int i; + unsigned short fcs; + unsigned char v; + + if (!scanhex(&adrs)) + return; + if (!scanhex(&ncsum)) + return; + fcs = 0xffff; + for (i = 0; i < ncsum; ++i) { + if (mread(adrs+i, &v, 1) == 0) { + printf("csum stopped at %x\n", adrs+i); + break; + } + fcs = FCS(fcs, v); + } + printf("%x\n", fcs); +} + +/* + * Check if this is a suitable place to put a breakpoint. + */ +static long check_bp_loc(unsigned long addr) +{ + unsigned int instr; + + addr &= ~3; + if (addr < KERNELBASE) { + printf("Breakpoints may only be placed at kernel addresses\n"); + return 0; + } + if (!mread(addr, &instr, sizeof(instr))) { + printf("Can't read instruction at address %lx\n", addr); + return 0; + } + if (IS_MTMSRD(instr) || IS_RFID(instr)) { + printf("Breakpoints may not be placed on mtmsrd or rfid " + "instructions\n"); + return 0; + } + return 1; +} + +static char *breakpoint_help_string = + "Breakpoint command usage:\n" + "b show breakpoints\n" + "b <addr> [cnt] set breakpoint at given instr addr\n" + "bc clear all breakpoints\n" + "bc <n/addr> clear breakpoint number n or at addr\n" + "bi <addr> [cnt] set hardware instr breakpoint (POWER3/RS64 only)\n" + "bd <addr> [cnt] set hardware data breakpoint\n" + ""; + +static void +bpt_cmds(void) +{ + int cmd; + unsigned long a; + int mode, i; + struct bpt *bp; + const char badaddr[] = "Only kernel addresses are permitted " + "for breakpoints\n"; + + cmd = inchar(); + switch (cmd) { +#ifndef CONFIG_8xx + case 'd': /* bd - hardware data breakpoint */ + mode = 7; + cmd = inchar(); + if (cmd == 'r') + mode = 5; + else if (cmd == 'w') + mode = 6; + else + termch = cmd; + dabr.address = 0; + dabr.enabled = 0; + if (scanhex(&dabr.address)) { + if (dabr.address < KERNELBASE) { + printf(badaddr); + break; + } + dabr.address &= ~7; + dabr.enabled = mode | BP_DABR; + } + break; + + case 'i': /* bi - hardware instr breakpoint */ + if (!cpu_has_feature(CPU_FTR_IABR)) { + printf("Hardware instruction breakpoint " + "not supported on this cpu\n"); + break; + } + if (iabr) { + iabr->enabled &= ~(BP_IABR | BP_IABR_TE); + iabr = NULL; + } + if (!scanhex(&a)) + break; + if (!check_bp_loc(a)) + break; + bp = new_breakpoint(a); + if (bp != NULL) { + bp->enabled |= BP_IABR | BP_IABR_TE; + iabr = bp; + } + break; +#endif + + case 'c': + if (!scanhex(&a)) { + /* clear all breakpoints */ + for (i = 0; i < NBPTS; ++i) + bpts[i].enabled = 0; + iabr = NULL; + dabr.enabled = 0; + printf("All breakpoints cleared\n"); + break; + } + + if (a <= NBPTS && a >= 1) { + /* assume a breakpoint number */ + bp = &bpts[a-1]; /* bp nums are 1 based */ + } else { + /* assume a breakpoint address */ + bp = at_breakpoint(a); + if (bp == 0) { + printf("No breakpoint at %x\n", a); + break; + } + } + + printf("Cleared breakpoint %x (", BP_NUM(bp)); + xmon_print_symbol(bp->address, " ", ")\n"); + bp->enabled = 0; + break; + + default: + termch = cmd; + cmd = skipbl(); + if (cmd == '?') { + printf(breakpoint_help_string); + break; + } + termch = cmd; + if (!scanhex(&a)) { + /* print all breakpoints */ + printf(" type address\n"); + if (dabr.enabled) { + printf(" data "REG" [", dabr.address); + if (dabr.enabled & 1) + printf("r"); + if (dabr.enabled & 2) + printf("w"); + printf("]\n"); + } + for (bp = bpts; bp < &bpts[NBPTS]; ++bp) { + if (!bp->enabled) + continue; + printf("%2x %s ", BP_NUM(bp), + (bp->enabled & BP_IABR)? "inst": "trap"); + xmon_print_symbol(bp->address, " ", "\n"); + } + break; + } + + if (!check_bp_loc(a)) + break; + bp = new_breakpoint(a); + if (bp != NULL) + bp->enabled |= BP_TRAP; + break; + } +} + +/* Very cheap human name for vector lookup. */ +static +const char *getvecname(unsigned long vec) +{ + char *ret; + + switch (vec) { + case 0x100: ret = "(System Reset)"; break; + case 0x200: ret = "(Machine Check)"; break; + case 0x300: ret = "(Data Access)"; break; + case 0x380: ret = "(Data SLB Access)"; break; + case 0x400: ret = "(Instruction Access)"; break; + case 0x480: ret = "(Instruction SLB Access)"; break; + case 0x500: ret = "(Hardware Interrupt)"; break; + case 0x600: ret = "(Alignment)"; break; + case 0x700: ret = "(Program Check)"; break; + case 0x800: ret = "(FPU Unavailable)"; break; + case 0x900: ret = "(Decrementer)"; break; + case 0xc00: ret = "(System Call)"; break; + case 0xd00: ret = "(Single Step)"; break; + case 0xf00: ret = "(Performance Monitor)"; break; + case 0xf20: ret = "(Altivec Unavailable)"; break; + case 0x1300: ret = "(Instruction Breakpoint)"; break; + default: ret = ""; + } + return ret; +} + +static void get_function_bounds(unsigned long pc, unsigned long *startp, + unsigned long *endp) +{ + unsigned long size, offset; + const char *name; + char *modname; + + *startp = *endp = 0; + if (pc == 0) + return; + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + name = kallsyms_lookup(pc, &size, &offset, &modname, tmpstr); + if (name != NULL) { + *startp = pc - offset; + *endp = pc - offset + size; + } + sync(); + } + catch_memory_errors = 0; +} + +static int xmon_depth_to_print = 64; + +#ifdef CONFIG_PPC64 +#define LRSAVE_OFFSET 0x10 +#define REG_FRAME_MARKER 0x7265677368657265ul /* "regshere" */ +#define MARKER_OFFSET 0x60 +#define REGS_OFFSET 0x70 +#else +#define LRSAVE_OFFSET 4 +#define REG_FRAME_MARKER 0x72656773 +#define MARKER_OFFSET 8 +#define REGS_OFFSET 16 +#endif + +static void xmon_show_stack(unsigned long sp, unsigned long lr, + unsigned long pc) +{ + unsigned long ip; + unsigned long newsp; + unsigned long marker; + int count = 0; + struct pt_regs regs; + + do { + if (sp < PAGE_OFFSET) { + if (sp != 0) + printf("SP (%lx) is in userspace\n", sp); + break; + } + + if (!mread(sp + LRSAVE_OFFSET, &ip, sizeof(unsigned long)) + || !mread(sp, &newsp, sizeof(unsigned long))) { + printf("Couldn't read stack frame at %lx\n", sp); + break; + } + + /* + * For the first stack frame, try to work out if + * LR and/or the saved LR value in the bottommost + * stack frame are valid. + */ + if ((pc | lr) != 0) { + unsigned long fnstart, fnend; + unsigned long nextip; + int printip = 1; + + get_function_bounds(pc, &fnstart, &fnend); + nextip = 0; + if (newsp > sp) + mread(newsp + LRSAVE_OFFSET, &nextip, + sizeof(unsigned long)); + if (lr == ip) { + if (lr < PAGE_OFFSET + || (fnstart <= lr && lr < fnend)) + printip = 0; + } else if (lr == nextip) { + printip = 0; + } else if (lr >= PAGE_OFFSET + && !(fnstart <= lr && lr < fnend)) { + printf("[link register ] "); + xmon_print_symbol(lr, " ", "\n"); + } + if (printip) { + printf("["REG"] ", sp); + xmon_print_symbol(ip, " ", " (unreliable)\n"); + } + pc = lr = 0; + + } else { + printf("["REG"] ", sp); + xmon_print_symbol(ip, " ", "\n"); + } + + /* Look for "regshere" marker to see if this is + an exception frame. */ + if (mread(sp + MARKER_OFFSET, &marker, sizeof(unsigned long)) + && marker == REG_FRAME_MARKER) { + if (mread(sp + REGS_OFFSET, ®s, sizeof(regs)) + != sizeof(regs)) { + printf("Couldn't read registers at %lx\n", + sp + REGS_OFFSET); + break; + } + printf("--- Exception: %lx %s at ", regs.trap, + getvecname(TRAP(®s))); + pc = regs.nip; + lr = regs.link; + xmon_print_symbol(pc, " ", "\n"); + } + + if (newsp == 0) + break; + + sp = newsp; + } while (count++ < xmon_depth_to_print); +} + +static void backtrace(struct pt_regs *excp) +{ + unsigned long sp; + + if (scanhex(&sp)) + xmon_show_stack(sp, 0, 0); + else + xmon_show_stack(excp->gpr[1], excp->link, excp->nip); + scannl(); +} + +static void print_bug_trap(struct pt_regs *regs) +{ + struct bug_entry *bug; + unsigned long addr; + + if (regs->msr & MSR_PR) + return; /* not in kernel */ + addr = regs->nip; /* address of trap instruction */ + if (addr < PAGE_OFFSET) + return; + bug = find_bug(regs->nip); + if (bug == NULL) + return; + if (bug->line & BUG_WARNING_TRAP) + return; + + printf("kernel BUG in %s at %s:%d!\n", + bug->function, bug->file, (unsigned int)bug->line); +} + +void excprint(struct pt_regs *fp) +{ + unsigned long trap; + +#ifdef CONFIG_SMP + printf("cpu 0x%x: ", smp_processor_id()); +#endif /* CONFIG_SMP */ + + trap = TRAP(fp); + printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(trap), fp); + printf(" pc: "); + xmon_print_symbol(fp->nip, ": ", "\n"); + + printf(" lr: ", fp->link); + xmon_print_symbol(fp->link, ": ", "\n"); + + printf(" sp: %lx\n", fp->gpr[1]); + printf(" msr: %lx\n", fp->msr); + + if (trap == 0x300 || trap == 0x380 || trap == 0x600) { + printf(" dar: %lx\n", fp->dar); + if (trap != 0x380) + printf(" dsisr: %lx\n", fp->dsisr); + } + + printf(" current = 0x%lx\n", current); +#ifdef CONFIG_PPC64 + printf(" paca = 0x%lx\n", get_paca()); +#endif + if (current) { + printf(" pid = %ld, comm = %s\n", + current->pid, current->comm); + } + + if (trap == 0x700) + print_bug_trap(fp); +} + +void prregs(struct pt_regs *fp) +{ + int n, trap; + unsigned long base; + struct pt_regs regs; + + if (scanhex(&base)) { + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + regs = *(struct pt_regs *)base; + sync(); + __delay(200); + } else { + catch_memory_errors = 0; + printf("*** Error reading registers from "REG"\n", + base); + return; + } + catch_memory_errors = 0; + fp = ®s; + } + +#ifdef CONFIG_PPC64 + if (FULL_REGS(fp)) { + for (n = 0; n < 16; ++n) + printf("R%.2ld = "REG" R%.2ld = "REG"\n", + n, fp->gpr[n], n+16, fp->gpr[n+16]); + } else { + for (n = 0; n < 7; ++n) + printf("R%.2ld = "REG" R%.2ld = "REG"\n", + n, fp->gpr[n], n+7, fp->gpr[n+7]); + } +#else + for (n = 0; n < 32; ++n) { + printf("R%.2d = %.8x%s", n, fp->gpr[n], + (n & 3) == 3? "\n": " "); + if (n == 12 && !FULL_REGS(fp)) { + printf("\n"); + break; + } + } +#endif + printf("pc = "); + xmon_print_symbol(fp->nip, " ", "\n"); + printf("lr = "); + xmon_print_symbol(fp->link, " ", "\n"); + printf("msr = "REG" cr = %.8lx\n", fp->msr, fp->ccr); + printf("ctr = "REG" xer = "REG" trap = %4lx\n", + fp->ctr, fp->xer, fp->trap); + trap = TRAP(fp); + if (trap == 0x300 || trap == 0x380 || trap == 0x600) + printf("dar = "REG" dsisr = %.8lx\n", fp->dar, fp->dsisr); +} + +void cacheflush(void) +{ + int cmd; + unsigned long nflush; + + cmd = inchar(); + if (cmd != 'i') + termch = cmd; + scanhex((void *)&adrs); + if (termch != '\n') + termch = 0; + nflush = 1; + scanhex(&nflush); + nflush = (nflush + L1_CACHE_BYTES - 1) / L1_CACHE_BYTES; + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + + if (cmd != 'i') { + for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES) + cflush((void *) adrs); + } else { + for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES) + cinval((void *) adrs); + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + } + catch_memory_errors = 0; +} + +unsigned long +read_spr(int n) +{ + unsigned int instrs[2]; + unsigned long (*code)(void); + unsigned long opd[3]; + unsigned long ret = -1UL; + + instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + opd[0] = (unsigned long)instrs; + opd[1] = 0; + opd[2] = 0; + store_inst(instrs); + store_inst(instrs+1); + code = (unsigned long (*)(void)) opd; + + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + + ret = code(); + + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } + + return ret; +} + +void +write_spr(int n, unsigned long val) +{ + unsigned int instrs[2]; + unsigned long (*code)(unsigned long); + unsigned long opd[3]; + + instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + opd[0] = (unsigned long)instrs; + opd[1] = 0; + opd[2] = 0; + store_inst(instrs); + store_inst(instrs+1); + code = (unsigned long (*)(unsigned long)) opd; + + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + + code(val); + + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } +} + +static unsigned long regno; +extern char exc_prolog; +extern char dec_exc; + +void super_regs(void) +{ + int cmd; + unsigned long val; +#ifdef CONFIG_PPC_ISERIES + struct paca_struct *ptrPaca = NULL; + struct lppaca *ptrLpPaca = NULL; + struct ItLpRegSave *ptrLpRegSave = NULL; +#endif + + cmd = skipbl(); + if (cmd == '\n') { + unsigned long sp, toc; + asm("mr %0,1" : "=r" (sp) :); + asm("mr %0,2" : "=r" (toc) :); + + printf("msr = "REG" sprg0= "REG"\n", + mfmsr(), mfspr(SPRN_SPRG0)); + printf("pvr = "REG" sprg1= "REG"\n", + mfspr(SPRN_PVR), mfspr(SPRN_SPRG1)); + printf("dec = "REG" sprg2= "REG"\n", + mfspr(SPRN_DEC), mfspr(SPRN_SPRG2)); + printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3)); + printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR)); +#ifdef CONFIG_PPC_ISERIES + // Dump out relevant Paca data areas. + printf("Paca: \n"); + ptrPaca = get_paca(); + + printf(" Local Processor Control Area (LpPaca): \n"); + ptrLpPaca = ptrPaca->lppaca_ptr; + printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n", + ptrLpPaca->saved_srr0, ptrLpPaca->saved_srr1); + printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n", + ptrLpPaca->saved_gpr3, ptrLpPaca->saved_gpr4); + printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->saved_gpr5); + + printf(" Local Processor Register Save Area (LpRegSave): \n"); + ptrLpRegSave = ptrPaca->reg_save_ptr; + printf(" Saved Sprg0=%.16lx Saved Sprg1=%.16lx \n", + ptrLpRegSave->xSPRG0, ptrLpRegSave->xSPRG0); + printf(" Saved Sprg2=%.16lx Saved Sprg3=%.16lx \n", + ptrLpRegSave->xSPRG2, ptrLpRegSave->xSPRG3); + printf(" Saved Msr =%.16lx Saved Nia =%.16lx \n", + ptrLpRegSave->xMSR, ptrLpRegSave->xNIA); +#endif + + return; + } + + scanhex(®no); + switch (cmd) { + case 'w': + val = read_spr(regno); + scanhex(&val); + write_spr(regno, val); + /* fall through */ + case 'r': + printf("spr %lx = %lx\n", regno, read_spr(regno)); + break; + } + scannl(); +} + +/* + * Stuff for reading and writing memory safely + */ +int +mread(unsigned long adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + p = (char *)adrs; + q = (char *)buf; + switch (size) { + case 2: + *(u16 *)q = *(u16 *)p; + break; + case 4: + *(u32 *)q = *(u32 *)p; + break; + case 8: + *(u64 *)q = *(u64 *)p; + break; + default: + for( ; n < size; ++n) { + *q++ = *p++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } + catch_memory_errors = 0; + return n; +} + +int +mwrite(unsigned long adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: + *(u16 *)p = *(u16 *)q; + break; + case 4: + *(u32 *)p = *(u32 *)q; + break; + case 8: + *(u64 *)p = *(u64 *)q; + break; + default: + for ( ; n < size; ++n) { + *p++ = *q++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } else { + printf("*** Error writing address %x\n", adrs + n); + } + catch_memory_errors = 0; + return n; +} + +static int fault_type; +static int fault_except; +static char *fault_chars[] = { "--", "**", "##" }; + +static int handle_fault(struct pt_regs *regs) +{ + fault_except = TRAP(regs); + switch (TRAP(regs)) { + case 0x200: + fault_type = 0; + break; + case 0x300: + case 0x380: + fault_type = 1; + break; + default: + fault_type = 2; + } + + longjmp(bus_error_jmp, 1); + + return 0; +} + +#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t)) + +void +byterev(unsigned char *val, int size) +{ + int t; + + switch (size) { + case 2: + SWAP(val[0], val[1], t); + break; + case 4: + SWAP(val[0], val[3], t); + SWAP(val[1], val[2], t); + break; + case 8: /* is there really any use for this? */ + SWAP(val[0], val[7], t); + SWAP(val[1], val[6], t); + SWAP(val[2], val[5], t); + SWAP(val[3], val[4], t); + break; + } +} + +static int brev; +static int mnoread; + +static char *memex_help_string = + "Memory examine command usage:\n" + "m [addr] [flags] examine/change memory\n" + " addr is optional. will start where left off.\n" + " flags may include chars from this set:\n" + " b modify by bytes (default)\n" + " w modify by words (2 byte)\n" + " l modify by longs (4 byte)\n" + " d modify by doubleword (8 byte)\n" + " r toggle reverse byte order mode\n" + " n do not read memory (for i/o spaces)\n" + " . ok to read (default)\n" + "NOTE: flags are saved as defaults\n" + ""; + +static char *memex_subcmd_help_string = + "Memory examine subcommands:\n" + " hexval write this val to current location\n" + " 'string' write chars from string to this location\n" + " ' increment address\n" + " ^ decrement address\n" + " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n" + " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n" + " ` clear no-read flag\n" + " ; stay at this addr\n" + " v change to byte mode\n" + " w change to word (2 byte) mode\n" + " l change to long (4 byte) mode\n" + " u change to doubleword (8 byte) mode\n" + " m addr change current addr\n" + " n toggle no-read flag\n" + " r toggle byte reverse flag\n" + " < count back up count bytes\n" + " > count skip forward count bytes\n" + " x exit this mode\n" + ""; + +void +memex(void) +{ + int cmd, inc, i, nslash; + unsigned long n; + unsigned char val[16]; + + scanhex((void *)&adrs); + cmd = skipbl(); + if (cmd == '?') { + printf(memex_help_string); + return; + } else { + termch = cmd; + } + last_cmd = "m\n"; + while ((cmd = skipbl()) != '\n') { + switch( cmd ){ + case 'b': size = 1; break; + case 'w': size = 2; break; + case 'l': size = 4; break; + case 'd': size = 8; break; + case 'r': brev = !brev; break; + case 'n': mnoread = 1; break; + case '.': mnoread = 0; break; + } + } + if( size <= 0 ) + size = 1; + else if( size > 8 ) + size = 8; + for(;;){ + if (!mnoread) + n = mread(adrs, val, size); + printf("%.16x%c", adrs, brev? 'r': ' '); + if (!mnoread) { + if (brev) + byterev(val, size); + putchar(' '); + for (i = 0; i < n; ++i) + printf("%.2x", val[i]); + for (; i < size; ++i) + printf("%s", fault_chars[fault_type]); + } + putchar(' '); + inc = size; + nslash = 0; + for(;;){ + if( scanhex(&n) ){ + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + inc = size; + } + cmd = skipbl(); + if (cmd == '\n') + break; + inc = 0; + switch (cmd) { + case '\'': + for(;;){ + n = inchar(); + if( n == '\\' ) + n = bsesc(); + else if( n == '\'' ) + break; + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + adrs += size; + } + adrs -= size; + inc = size; + break; + case ',': + adrs += size; + break; + case '.': + mnoread = 0; + break; + case ';': + break; + case 'x': + case EOF: + scannl(); + return; + case 'b': + case 'v': + size = 1; + break; + case 'w': + size = 2; + break; + case 'l': + size = 4; + break; + case 'u': + size = 8; + break; + case '^': + adrs -= size; + break; + break; + case '/': + if (nslash > 0) + adrs -= 1 << nslash; + else + nslash = 0; + nslash += 4; + adrs += 1 << nslash; + break; + case '\\': + if (nslash < 0) + adrs += 1 << -nslash; + else + nslash = 0; + nslash -= 4; + adrs -= 1 << -nslash; + break; + case 'm': + scanhex((void *)&adrs); + break; + case 'n': + mnoread = 1; + break; + case 'r': + brev = !brev; + break; + case '<': + n = size; + scanhex(&n); + adrs -= n; + break; + case '>': + n = size; + scanhex(&n); + adrs += n; + break; + case '?': + printf(memex_subcmd_help_string); + break; + } + } + adrs += inc; + } +} + +int +bsesc(void) +{ + int c; + + c = inchar(); + switch( c ){ + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 'b': c = '\b'; break; + case 't': c = '\t'; break; + } + return c; +} + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) +void +dump(void) +{ + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&adrs); + if (termch != '\n') + termch = 0; + if (c == 'i') { + scanhex(&nidump); + if (nidump == 0) + nidump = 16; + else if (nidump > MAX_DUMP) + nidump = MAX_DUMP; + adrs += ppc_inst_dump(adrs, nidump, 1); + last_cmd = "di\n"; + } else { + scanhex(&ndump); + if (ndump == 0) + ndump = 64; + else if (ndump > MAX_DUMP) + ndump = MAX_DUMP; + prdump(adrs, ndump); + adrs += ndump; + last_cmd = "d\n"; + } +} + +void +prdump(unsigned long adrs, long ndump) +{ + long n, m, c, r, nr; + unsigned char temp[16]; + + for (n = ndump; n > 0;) { + printf(REG, adrs); + putchar(' '); + r = n < 16? n: 16; + nr = mread(adrs, temp, r); + adrs += nr; + for (m = 0; m < r; ++m) { + if ((m & 7) == 0 && m > 0) + putchar(' '); + if (m < nr) + printf("%.2x", temp[m]); + else + printf("%s", fault_chars[fault_type]); + } + if (m <= 8) + printf(" "); + for (; m < 16; ++m) + printf(" "); + printf(" |"); + for (m = 0; m < r; ++m) { + if (m < nr) { + c = temp[m]; + putchar(' ' <= c && c <= '~'? c: '.'); + } else + putchar(' '); + } + n -= r; + for (; m < 16; ++m) + putchar(' '); + printf("|\n"); + if (nr < r) + break; + } +} + +int +ppc_inst_dump(unsigned long adr, long count, int praddr) +{ + int nr, dotted; + unsigned long first_adr; + unsigned long inst, last_inst = 0; + unsigned char val[4]; + + dotted = 0; + for (first_adr = adr; count > 0; --count, adr += 4) { + nr = mread(adr, val, 4); + if (nr == 0) { + if (praddr) { + const char *x = fault_chars[fault_type]; + printf(REG" %s%s%s%s\n", adr, x, x, x, x); + } + break; + } + inst = GETWORD(val); + if (adr > first_adr && inst == last_inst) { + if (!dotted) { + printf(" ...\n"); + dotted = 1; + } + continue; + } + dotted = 0; + last_inst = inst; + if (praddr) + printf(REG" %.8x", adr, inst); + printf("\t"); + print_insn_powerpc(inst, adr, 0); /* always returns 4 */ + printf("\n"); + } + return adr - first_adr; +} + +void +print_address(unsigned long addr) +{ + xmon_print_symbol(addr, "\t# ", ""); +} + + +/* + * Memory operations - move, set, print differences + */ +static unsigned long mdest; /* destination address */ +static unsigned long msrc; /* source address */ +static unsigned long mval; /* byte value to set memory to */ +static unsigned long mcount; /* # bytes to affect */ +static unsigned long mdiffs; /* max # differences to print */ + +void +memops(int cmd) +{ + scanhex((void *)&mdest); + if( termch != '\n' ) + termch = 0; + scanhex((void *)(cmd == 's'? &mval: &msrc)); + if( termch != '\n' ) + termch = 0; + scanhex((void *)&mcount); + switch( cmd ){ + case 'm': + memmove((void *)mdest, (void *)msrc, mcount); + break; + case 's': + memset((void *)mdest, mval, mcount); + break; + case 'd': + if( termch != '\n' ) + termch = 0; + scanhex((void *)&mdiffs); + memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs); + break; + } +} + +void +memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr) +{ + unsigned n, prt; + + prt = 0; + for( n = nb; n > 0; --n ) + if( *p1++ != *p2++ ) + if( ++prt <= maxpr ) + printf("%.16x %.2x # %.16x %.2x\n", p1 - 1, + p1[-1], p2 - 1, p2[-1]); + if( prt > maxpr ) + printf("Total of %d differences\n", prt); +} + +static unsigned mend; +static unsigned mask; + +void +memlocate(void) +{ + unsigned a, n; + unsigned char val[4]; + + last_cmd = "ml"; + scanhex((void *)&mdest); + if (termch != '\n') { + termch = 0; + scanhex((void *)&mend); + if (termch != '\n') { + termch = 0; + scanhex((void *)&mval); + mask = ~0; + if (termch != '\n') termch = 0; + scanhex((void *)&mask); + } + } + n = 0; + for (a = mdest; a < mend; a += 4) { + if (mread(a, val, 4) == 4 + && ((GETWORD(val) ^ mval) & mask) == 0) { + printf("%.16x: %.16x\n", a, GETWORD(val)); + if (++n >= 10) + break; + } + } +} + +static unsigned long mskip = 0x1000; +static unsigned long mlim = 0xffffffff; + +void +memzcan(void) +{ + unsigned char v; + unsigned a; + int ok, ook; + + scanhex(&mdest); + if (termch != '\n') termch = 0; + scanhex(&mskip); + if (termch != '\n') termch = 0; + scanhex(&mlim); + ook = 0; + for (a = mdest; a < mlim; a += mskip) { + ok = mread(a, &v, 1); + if (ok && !ook) { + printf("%.8x .. ", a); + fflush(stdout); + } else if (!ok && ook) + printf("%.8x\n", a - mskip); + ook = ok; + if (a + mskip < a) + break; + } + if (ook) + printf("%.8x\n", a - mskip); +} + +void proccall(void) +{ + unsigned long args[8]; + unsigned long ret; + int i; + typedef unsigned long (*callfunc_t)(unsigned long, unsigned long, + unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, unsigned long); + callfunc_t func; + + if (!scanhex(&adrs)) + return; + if (termch != '\n') + termch = 0; + for (i = 0; i < 8; ++i) + args[i] = 0; + for (i = 0; i < 8; ++i) { + if (!scanhex(&args[i]) || termch == '\n') + break; + termch = 0; + } + func = (callfunc_t) adrs; + ret = 0; + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + ret = func(args[0], args[1], args[2], args[3], + args[4], args[5], args[6], args[7]); + sync(); + printf("return value is %x\n", ret); + } else { + printf("*** %x exception occurred\n", fault_except); + } + catch_memory_errors = 0; +} + +/* Input scanning routines */ +int +skipbl(void) +{ + int c; + + if( termch != 0 ){ + c = termch; + termch = 0; + } else + c = inchar(); + while( c == ' ' || c == '\t' ) + c = inchar(); + return c; +} + +#define N_PTREGS 44 +static char *regnames[N_PTREGS] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "pc", "msr", "or3", "ctr", "lr", "xer", "ccr", +#ifdef CONFIG_PPC64 + "softe", +#else + "mq", +#endif + "trap", "dar", "dsisr", "res" +}; + +int +scanhex(unsigned long *vp) +{ + int c, d; + unsigned long v; + + c = skipbl(); + if (c == '%') { + /* parse register name */ + char regname[8]; + int i; + + for (i = 0; i < sizeof(regname) - 1; ++i) { + c = inchar(); + if (!isalnum(c)) { + termch = c; + break; + } + regname[i] = c; + } + regname[i] = 0; + for (i = 0; i < N_PTREGS; ++i) { + if (strcmp(regnames[i], regname) == 0) { + if (xmon_regs == NULL) { + printf("regs not available\n"); + return 0; + } + *vp = ((unsigned long *)xmon_regs)[i]; + return 1; + } + } + printf("invalid register name '%%%s'\n", regname); + return 0; + } + + /* skip leading "0x" if any */ + + if (c == '0') { + c = inchar(); + if (c == 'x') { + c = inchar(); + } else { + d = hexdigit(c); + if (d == EOF) { + termch = c; + *vp = 0; + return 1; + } + } + } else if (c == '$') { + int i; + for (i=0; i<63; i++) { + c = inchar(); + if (isspace(c)) { + termch = c; + break; + } + tmpstr[i] = c; + } + tmpstr[i++] = 0; + *vp = 0; + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + *vp = kallsyms_lookup_name(tmpstr); + sync(); + } + catch_memory_errors = 0; + if (!(*vp)) { + printf("unknown symbol '%s'\n", tmpstr); + return 0; + } + return 1; + } + + d = hexdigit(c); + if (d == EOF) { + termch = c; + return 0; + } + v = 0; + do { + v = (v << 4) + d; + c = inchar(); + d = hexdigit(c); + } while (d != EOF); + termch = c; + *vp = v; + return 1; +} + +void +scannl(void) +{ + int c; + + c = termch; + termch = 0; + while( c != '\n' ) + c = inchar(); +} + +int hexdigit(int c) +{ + if( '0' <= c && c <= '9' ) + return c - '0'; + if( 'A' <= c && c <= 'F' ) + return c - ('A' - 10); + if( 'a' <= c && c <= 'f' ) + return c - ('a' - 10); + return EOF; +} + +void +getstring(char *s, int size) +{ + int c; + + c = skipbl(); + do { + if( size > 1 ){ + *s++ = c; + --size; + } + c = inchar(); + } while( c != ' ' && c != '\t' && c != '\n' ); + termch = c; + *s = 0; +} + +static char line[256]; +static char *lineptr; + +void +flush_input(void) +{ + lineptr = NULL; +} + +int +inchar(void) +{ + if (lineptr == NULL || *lineptr == 0) { + if (fgets(line, sizeof(line), stdin) == NULL) { + lineptr = NULL; + return EOF; + } + lineptr = line; + } + return *lineptr++; +} + +void +take_input(char *str) +{ + lineptr = str; +} + + +static void +symbol_lookup(void) +{ + int type = inchar(); + unsigned long addr; + static char tmp[64]; + + switch (type) { + case 'a': + if (scanhex(&addr)) + xmon_print_symbol(addr, ": ", "\n"); + termch = 0; + break; + case 's': + getstring(tmp, 64); + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + addr = kallsyms_lookup_name(tmp); + if (addr) + printf("%s: %lx\n", tmp, addr); + else + printf("Symbol '%s' not found.\n", tmp); + sync(); + } + catch_memory_errors = 0; + termch = 0; + break; + } +} + + +/* Print an address in numeric and symbolic form (if possible) */ +static void xmon_print_symbol(unsigned long address, const char *mid, + const char *after) +{ + char *modname; + const char *name = NULL; + unsigned long offset, size; + + printf(REG, address); + if (setjmp(bus_error_jmp) == 0) { + catch_memory_errors = 1; + sync(); + name = kallsyms_lookup(address, &size, &offset, &modname, + tmpstr); + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + } + + catch_memory_errors = 0; + + if (name) { + printf("%s%s+%#lx/%#lx", mid, name, offset, size); + if (modname) + printf(" [%s]", modname); + } + printf("%s", after); +} + +#ifdef CONFIG_PPC64 +static void dump_slb(void) +{ + int i; + unsigned long tmp; + + printf("SLB contents of cpu %x\n", smp_processor_id()); + + for (i = 0; i < SLB_NUM_ENTRIES; i++) { + asm volatile("slbmfee %0,%1" : "=r" (tmp) : "r" (i)); + printf("%02d %016lx ", i, tmp); + + asm volatile("slbmfev %0,%1" : "=r" (tmp) : "r" (i)); + printf("%016lx\n", tmp); + } +} + +static void dump_stab(void) +{ + int i; + unsigned long *tmp = (unsigned long *)get_paca()->stab_addr; + + printf("Segment table contents of cpu %x\n", smp_processor_id()); + + for (i = 0; i < PAGE_SIZE/16; i++) { + unsigned long a, b; + + a = *tmp++; + b = *tmp++; + + if (a || b) { + printf("%03d %016lx ", i, a); + printf("%016lx\n", b); + } + } +} + +void dump_segments(void) +{ + if (cpu_has_feature(CPU_FTR_SLB)) + dump_slb(); + else + dump_stab(); +} +#endif + +#ifdef CONFIG_PPC_STD_MMU_32 +void dump_segments(void) +{ + int i; + + printf("sr0-15 ="); + for (i = 0; i < 16; ++i) + printf(" %x", mfsrin(i)); + printf("\n"); +} +#endif + +void xmon_init(int enable) +{ + if (enable) { + __debugger = xmon; + __debugger_ipi = xmon_ipi; + __debugger_bpt = xmon_bpt; + __debugger_sstep = xmon_sstep; + __debugger_iabr_match = xmon_iabr_match; + __debugger_dabr_match = xmon_dabr_match; + __debugger_fault_handler = xmon_fault_handler; + } else { + __debugger = NULL; + __debugger_ipi = NULL; + __debugger_bpt = NULL; + __debugger_sstep = NULL; + __debugger_iabr_match = NULL; + __debugger_dabr_match = NULL; + __debugger_fault_handler = NULL; + } +} |