patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / ppc64 / xmon / xmon.c
index 90fe14b..97b1509 100644 (file)
 
 #ifdef CONFIG_SMP
 volatile cpumask_t cpus_in_xmon = CPU_MASK_NONE;
-static unsigned long got_xmon = 0;
-static volatile int take_xmon = -1;
-static volatile int leaving_xmon = 0;
+static unsigned long xmon_taken = 1;
+static int xmon_owner;
+static int xmon_gate;
 #endif /* CONFIG_SMP */
 
+#define TRAP(regs)     ((regs)->trap)
+#define FULL_REGS(regs)        1
+
+static unsigned long in_xmon = 0;
+
 static unsigned long adrs;
 static int size = 1;
 static unsigned long ndump = 64;
@@ -52,24 +57,39 @@ static unsigned long ncsum = 4096;
 static int termch;
 static char tmpstr[128];
 
-static u_int bus_error_jmp[100];
+#define JMP_BUF_LEN    (184/sizeof(long))
+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 instr;
-       unsigned long count;
-       unsigned char enabled;
+       unsigned long   address;
+       unsigned int    instr[2];
+       atomic_t        ref_count;
+       int             enabled;
+       unsigned long   pad;
 };
 
-#define NBPTS  16
+/* 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 struct bpt *iabr;
 static unsigned bpinstr = 0x7fe00008;  /* trap */
 
+#define BP_NUM(bp)     ((bp) - bpts + 1)
+
+/* Bits in SRR1 that are copied from MSR */
+#define MSR_MASK       0xffffffff87c0ffff
+
 /* Prototypes */
 static int cmds(struct pt_regs *);
 static int mread(unsigned long, void *, int);
@@ -80,7 +100,7 @@ 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);
+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 *);
@@ -97,34 +117,40 @@ 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 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);
-#ifdef CONFIG_SMP
-static void cpu_cmd(void);
-#endif /* CONFIG_SMP */
+static int  cpu_cmd(void);
 static void csum(void);
 static void bootcmds(void);
 void dump_segments(void);
 static void symbol_lookup(void);
+static int emulate_step(struct pt_regs *regs, unsigned int instr);
+static void xmon_print_symbol(unsigned long address, const char *mid,
+                             const char *after);
+static const char *getvecname(unsigned long vec);
 
 static void debug_trace(void);
 
-extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned long);
+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 int xmon_read_poll(void);
-extern int setjmp(u_int *);
-extern void longjmp(u_int *, int);
+extern int setjmp(long *);
+extern void longjmp(long *, int);
 extern unsigned long _ASR;
+extern char SystemCall_common[];
 
 pte_t *find_linux_pte(pgd_t *pgdir, unsigned long va); /* from htab.c */
 
@@ -147,7 +173,6 @@ Commands:\n\
 #ifdef CONFIG_SMP
   "\
   c    print cpus stopped in xmon\n\
-  ci   send xmon interrupt to all other cpus\n\
   c#   try to switch to cpu number h (in hex)\n"
 #endif
   "\
@@ -182,22 +207,8 @@ Commands:\n\
   zh   halt\n"
 ;
 
-static int xmon_trace[NR_CPUS];
-#define SSTEP  1               /* stepping because of 's' command */
-#define BRSTEP 2               /* stepping over breakpoint */
-
-static struct pt_regs *xmon_regs[NR_CPUS];
-
-void __xmon_print_symbol(const char *fmt, unsigned long address);
-#define xmon_print_symbol(fmt, addr)           \
-do {                                           \
-       __check_printsym_format(fmt, "");       \
-       __xmon_print_symbol(fmt, addr);         \
-} while(0)
+static struct pt_regs *xmon_regs;
 
-/*
- * Stuff for reading and writing memory safely
- */
 extern inline void sync(void)
 {
        asm volatile("sync; isync");
@@ -223,22 +234,278 @@ extern inline void sync(void)
  no functions have been called from the current function.
  */
 
+/*
+ * We don't allow single-stepping an mtmsrd that would clear
+ * MSR_RI, since that would make the exception unrecoverable.
+ * Since we need to single-step to proceed from a breakpoint,
+ * we don't allow putting a breakpoint on an mtmsrd instruction.
+ * Similarly we don't allow breakpoints on rfid instructions.
+ * These macros tell us if an instruction is a mtmsrd or rfid.
+ */
+#define IS_MTMSRD(instr)       (((instr) & 0xfc0007fe) == 0x7c000164)
+#define IS_RFID(instr)         (((instr) & 0xfc0007fe) == 0x4c000024)
+
+/*
+ * 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)
 {
 #ifndef CONFIG_PPC_ISERIES
-        rtas_call(rtas_token("set-indicator"), 3, 1, NULL, SURVEILLANCE_TOKEN,
-                 0, 0);
+       /* Since this can't be a module, args should end up below 4GB. */
+       static struct rtas_args args;
+
+       if (systemcfg->platform & PLATFORM_PSERIES) {
+               /*
+                * 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
 }
 
-int
-xmon(struct pt_regs *excp)
+#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)
 {
-       struct pt_regs regs;
        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 = get_msr();
+       set_msrd(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)));
+               longjmp(xmon_fault_jmp[cpu], 1);
+       }
+
+       if (setjmp(recurse_jmp) != 0) {
+               if (!in_xmon || !xmon_gate) {
+                       printf("xmon: WARNING: bad recursive fault "
+                              "on cpu 0x%x\n", cpu);
+                       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;
+               }
+               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);
+                       }
+               }
+       }
+
+       insert_cpu_bpts();
+
+       set_msrd(msr);          /* restore interrupt enable */
+
+       return cmd != 'X';
+}
+
+int xmon(struct pt_regs *excp)
+{
+       struct pt_regs regs;
 
        if (excp == NULL) {
                /* Ok, grab regs as they are now.
@@ -289,142 +556,95 @@ xmon(struct pt_regs *excp)
                regs.trap = 0;
                excp = &regs;
        }
+       return xmon_core(excp, 0);
+}
 
-       msr = get_msr();
-       set_msrd(msr & ~MSR_EE);        /* disable interrupts */
-       xmon_regs[smp_processor_id()] = excp;
-       excprint(excp);
-#ifdef CONFIG_SMP
-       leaving_xmon = 0;
-       /* possible race condition here if a CPU is held up and gets
-        * here while we are exiting */
-       if (cpu_test_and_set(smp_processor_id(), cpus_in_xmon)) {
-               /* xmon probably caused an exception itself */
-               printf("We are already in xmon\n");
-               for (;;)
-                       cpu_relax();
-       }
-       while (test_and_set_bit(0, &got_xmon)) {
-               if (take_xmon == smp_processor_id()) {
-                       take_xmon = -1;
-                       break;
-               }
-               cpu_relax();
-       }
-       /*
-        * XXX: breakpoints are removed while any cpu is in xmon
-        */
-#endif /* CONFIG_SMP */
-       remove_bpts();
-       disable_surveillance();
-       printf("press ? for help ");
-       cmd = cmds(excp);
-       if (cmd == 's') {
-               xmon_trace[smp_processor_id()] = SSTEP;
-               excp->msr |= MSR_SE;
-#ifdef CONFIG_SMP              
-               take_xmon = smp_processor_id();
-#endif         
-       } else if (at_breakpoint(excp->nip)) {
-               xmon_trace[smp_processor_id()] = BRSTEP;
-               excp->msr |= MSR_SE;
-       } else {
-               xmon_trace[smp_processor_id()] = 0;
-               insert_bpts();
+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;
        }
-       xmon_regs[smp_processor_id()] = 0;
-#ifdef CONFIG_SMP
-       leaving_xmon = 1;
-       if (cmd != 's')
-               clear_bit(0, &got_xmon);
-       cpu_clear(smp_processor_id(), cpus_in_xmon);
-#endif /* CONFIG_SMP */
-       set_msrd(msr);          /* restore interrupt enable */
 
-       if (cmd == 'X')
+       /* Are we at a breakpoint? */
+       bp = at_breakpoint(regs->nip);
+       if (!bp)
                return 0;
 
+       xmon_core(regs, 0);
+
        return 1;
 }
 
-int
-xmon_bpt(struct pt_regs *regs)
+int xmon_sstep(struct pt_regs *regs)
 {
-       struct bpt *bp;
-
-       bp = at_breakpoint(regs->nip);
-       if (!bp)
+       if (user_mode(regs))
                return 0;
-       if (bp->count) {
-               --bp->count;
-               remove_bpts();
-               excprint(regs);
-               xmon_trace[smp_processor_id()] = BRSTEP;
-               regs->msr |= MSR_SE;
-       } else {
-               printf("Stopped at breakpoint %x (%lx ", (bp - bpts) + 1,
-                       bp->address);
-               xmon_print_symbol("%s)\n", bp->address);
-               xmon(regs);
-       }
+       xmon_core(regs, 0);
        return 1;
 }
 
-int
-xmon_sstep(struct pt_regs *regs)
+int xmon_dabr_match(struct pt_regs *regs)
 {
-       if (!xmon_trace[smp_processor_id()])
+       if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
                return 0;
-       if (xmon_trace[smp_processor_id()] == BRSTEP) {
-               xmon_trace[smp_processor_id()] = 0;
-               insert_bpts();
-       } else {
-               xmon(regs);
-       }
+       xmon_core(regs, 0);
        return 1;
 }
 
-int
-xmon_dabr_match(struct pt_regs *regs)
+int xmon_iabr_match(struct pt_regs *regs)
 {
-       if (dabr.enabled && dabr.count) {
-               --dabr.count;
-               remove_bpts();
-               excprint(regs);
-               xmon_trace[smp_processor_id()] = BRSTEP;
-               regs->msr |= MSR_SE;
-       } else {
-               dabr.instr = regs->nip;
-               xmon(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_iabr_match(struct pt_regs *regs)
+int xmon_ipi(struct pt_regs *regs)
 {
-       if (iabr.enabled && iabr.count) {
-               --iabr.count;
-               remove_bpts();
-               excprint(regs);
-               xmon_trace[smp_processor_id()] = BRSTEP;
-               regs->msr |= MSR_SE;
-       } else {
-               xmon(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 1;
+
+       return 0;
 }
 
-static struct bpt *
-at_breakpoint(unsigned long pc)
+
+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)
@@ -432,73 +652,228 @@ at_breakpoint(unsigned long pc)
        return 0;
 }
 
-static void
-insert_bpts()
+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)
+               if ((bp->enabled & (BP_TRAP|BP_IABR)) == 0)
                        continue;
-               if (mread(bp->address, &bp->instr, 4) != 4
-                   || mwrite(bp->address, &bpinstr, 4) != 4) {
-                       printf("Couldn't insert breakpoint at %x, disabling\n",
-                              bp->address);
+               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;
-               } else {
-                       store_inst((void *)bp->address);
+                       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);
-       if ((cur_cpu_spec->cpu_features & CPU_FTR_IABR) && iabr.enabled)
-               set_iabr(iabr.address);
+               set_dabr(dabr.address | (dabr.enabled & 7));
+       if (iabr && (cur_cpu_spec->cpu_features & CPU_FTR_IABR))
+               set_iabr(iabr->address
+                        | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
 }
 
-static void
-remove_bpts()
+static void remove_bpts(void)
 {
        int i;
        struct bpt *bp;
        unsigned instr;
 
-       set_dabr(0);
-       if ((cur_cpu_spec->cpu_features & CPU_FTR_IABR))
-               set_iabr(0);
-
        bp = bpts;
        for (i = 0; i < NBPTS; ++i, ++bp) {
-               if (!bp->enabled)
+               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 %x\n",
+                       printf("Couldn't remove breakpoint at %lx\n",
                               bp->address);
                else
                        store_inst((void *)bp->address);
        }
 }
 
-static char *last_cmd;
+static void remove_cpu_bpts(void)
+{
+       set_dabr(0);
+       if ((cur_cpu_spec->cpu_features & CPU_FTR_IABR))
+               set_iabr(0);
+}
+
+static int branch_taken(unsigned int instr, struct pt_regs *regs)
+{
+       unsigned int bo = (instr >> 21) & 0x1f;
+       unsigned int bi;
+
+       if ((bo & 4) == 0) {
+               /* decrement counter */
+               --regs->ctr;
+               if (((bo >> 1) & 1) ^ (regs->ctr == 0))
+                       return 0;
+       }
+       if ((bo & 0x10) == 0) {
+               /* check bit from CR */
+               bi = (instr >> 16) & 0x1f;
+               if (((regs->ccr >> (31 - bi)) & 1) != ((bo >> 3) & 1))
+                       return 0;
+       }
+       return 1;
+}
+
+/*
+ * Emulate instructions that cause a transfer of control.
+ * Returns 1 if the step was emulated, 0 if not,
+ * or -1 if the instruction is one that should not be stepped,
+ * such as an rfid, or a mtmsrd that would clear MSR_RI.
+ */
+static int emulate_step(struct pt_regs *regs, unsigned int instr)
+{
+       unsigned int opcode, rd;
+       unsigned long int imm;
+
+       opcode = instr >> 26;
+       switch (opcode) {
+       case 16:        /* bc */
+               imm = (signed short)(instr & 0xfffc);
+               if ((instr & 2) == 0)
+                       imm += regs->nip;
+               regs->nip += 4;         /* XXX check 32-bit mode */
+               if (instr & 1)
+                       regs->link = regs->nip;
+               if (branch_taken(instr, regs))
+                       regs->nip = imm;
+               return 1;
+       case 17:        /* sc */
+               regs->gpr[9] = regs->gpr[13];
+               regs->gpr[11] = regs->nip + 4;
+               regs->gpr[12] = regs->msr & MSR_MASK;
+               regs->gpr[13] = (unsigned long) get_paca();
+               regs->nip = (unsigned long) &SystemCall_common;
+               regs->msr = MSR_KERNEL;
+               return 1;
+       case 18:        /* b */
+               imm = instr & 0x03fffffc;
+               if (imm & 0x02000000)
+                       imm -= 0x04000000;
+               if ((instr & 2) == 0)
+                       imm += regs->nip;
+               if (instr & 1)
+                       regs->link = regs->nip + 4;
+               regs->nip = imm;
+               return 1;
+       case 19:
+               switch (instr & 0x7fe) {
+               case 0x20:      /* bclr */
+               case 0x420:     /* bcctr */
+                       imm = (instr & 0x400)? regs->ctr: regs->link;
+                       regs->nip += 4;         /* XXX check 32-bit mode */
+                       if (instr & 1)
+                               regs->link = regs->nip;
+                       if (branch_taken(instr, regs))
+                               regs->nip = imm;
+                       return 1;
+               case 0x24:      /* rfid, scary */
+                       printf("Can't single-step an rfid instruction\n");
+                       return -1;
+               }
+       case 31:
+               rd = (instr >> 21) & 0x1f;
+               switch (instr & 0x7fe) {
+               case 0xa6:      /* mfmsr */
+                       regs->gpr[rd] = regs->msr & MSR_MASK;
+                       regs->nip += 4;
+                       return 1;
+               case 0x164:     /* mtmsrd */
+                       /* only MSR_EE and MSR_RI get changed if bit 15 set */
+                       /* mtmsrd doesn't change MSR_HV and MSR_ME */
+                       imm = (instr & 0x10000)? 0x8002: 0xefffffffffffefffUL;
+                       imm = (regs->msr & MSR_MASK & ~imm)
+                               | (regs->gpr[rd] & imm);
+                       if ((imm & MSR_RI) == 0) {
+                               printf("Can't step an instruction that would "
+                                      "clear MSR.RI\n");
+                               return -1;
+                       }
+                       regs->msr = imm;
+                       regs->nip += 4;
+                       return 1;
+               }
+       }
+       return 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
-               /* Need to check if we should take any commands on
-                  this CPU. */
-               if (leaving_xmon)
-                       return cmd;
-               printf("%d:", smp_processor_id());
+               printf("%x:", smp_processor_id());
 #endif /* CONFIG_SMP */
                printf("mon> ");
                fflush(stdout);
@@ -545,10 +920,7 @@ cmds(struct pt_regs *excp)
                        prregs(excp);   /* print regs */
                        break;
                case 'e':
-                       if (excp == NULL)
-                               printf("No exception information\n");
-                       else
-                               excprint(excp);
+                       excprint(excp);
                        break;
                case 'S':
                        super_regs();
@@ -560,6 +932,9 @@ cmds(struct pt_regs *excp)
                        cacheflush();
                        break;
                case 's':
+                       if (do_step(excp))
+                               return cmd;
+                       break;
                case 'x':
                case 'X':
                case EOF:
@@ -576,13 +951,13 @@ cmds(struct pt_regs *excp)
                case 'C':
                        csum();
                        break;
-#ifdef CONFIG_SMP
                case 'c':
-                       cpu_cmd();
+                       if (cpu_cmd())
+                               return 0;
                        break;
-#endif /* CONFIG_SMP */
                case 'z':
                        bootcmds();
+                       break;
                case 'T':
                        debug_trace();
                        break;
@@ -592,7 +967,7 @@ cmds(struct pt_regs *excp)
                default:
                        printf("Unrecognized command: ");
                        do {
-                               if( ' ' < cmd && cmd <= '~' )
+                               if (' ' < cmd && cmd <= '~')
                                        putchar(cmd);
                                else
                                        printf("\\x%x", cmd);
@@ -601,10 +976,37 @@ cmds(struct pt_regs *excp)
                        printf(" (type ? for help)\n");
                        break;
                }
-               cpu_relax();
        }
 }
 
+/*
+ * 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)
+                               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;
@@ -618,56 +1020,59 @@ static void bootcmds(void)
                ppc_md.power_off();
 }
 
-#ifdef CONFIG_SMP
-static void cpu_cmd(void)
+static int cpu_cmd(void)
 {
+#ifdef CONFIG_SMP
        unsigned long cpu;
        int timeout;
-       int cmd;
+       int count;
 
-       cmd = inchar();
-       if (cmd == 'i') {
-               printf("stopping all cpus\n");
-               /* interrupt other cpu(s) */
-               cpu = MSG_ALL_BUT_SELF;
-               smp_send_debugger_break(cpu);
-               return;
-       }
-       termch = cmd;
        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)) {
-                               printf(" %x", cpu);
-                               if (cpu == smp_processor_id())
-                                       printf("*", cpu);
+                               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;
+               return 0;
        }
        /* try to switch to cpu specified */
-       take_xmon = cpu;
+       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 (take_xmon >= 0) {
+       while (!xmon_taken) {
                if (--timeout == 0) {
-                       /* yes there's a race here */
-                       take_xmon = -1;
+                       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;
-               }
-       }
-       /* now have to wait to be given control back */
-       while (test_and_set_bit(0, &got_xmon)) {
-               if (take_xmon == smp_processor_id()) {
-                       take_xmon = -1;
-                       break;
+                       return 0;
                }
-               cpu_relax();
+               barrier();
        }
-}
+       return 1;
+#else
+       return 0;
 #endif /* CONFIG_SMP */
+}
 
 static unsigned short fcstab[256] = {
        0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
@@ -728,6 +1133,30 @@ csum(void)
        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"
@@ -745,6 +1174,8 @@ bpt_cmds(void)
        unsigned long a;
        int mode, i;
        struct bpt *bp;
+       const char badaddr[] = "Only kernel addresses are permitted "
+               "for breakpoints\n";
 
        cmd = inchar();
        switch (cmd) {
@@ -758,54 +1189,66 @@ bpt_cmds(void)
                else
                        termch = cmd;
                dabr.address = 0;
-               dabr.count = 0;
-               dabr.enabled = scanhex(&dabr.address);
-               scanhex(&dabr.count);
-               if (dabr.enabled)
-                       dabr.address = (dabr.address & ~7) | mode;
+               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 (!(cur_cpu_spec->cpu_features & CPU_FTR_IABR)) {
-                       printf("Not implemented on POWER4\n");
+                       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;
                }
-               iabr.address = 0;
-               iabr.count = 0;
-               iabr.enabled = scanhex(&iabr.address);
-               if (iabr.enabled)
-                       iabr.address |= 3;
-               scanhex(&iabr.count);
                break;
+
        case 'c':
                if (!scanhex(&a)) {
                        /* clear all breakpoints */
                        for (i = 0; i < NBPTS; ++i)
                                bpts[i].enabled = 0;
-                       iabr.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 {
-                       if (a <= NBPTS && a >= 1) {
-                               /* assume a breakpoint number */
-                               --a;    /* bp nums are 1 based */
-                               bp = &bpts[a];
-                       } else {
-                               /* assume a breakpoint address */
-                               bp = at_breakpoint(a);
-                       }
+                       /* assume a breakpoint address */
+                       bp = at_breakpoint(a);
                        if (bp == 0) {
                                printf("No breakpoint at %x\n", a);
-                       } else {
-                               printf("Cleared breakpoint %x (%lx ", 
-                                       (bp - bpts) + 1, bp->address);
-                               xmon_print_symbol("%s)\n", bp->address);
-                               bp->enabled = 0;
+                               break;
                        }
                }
+
+               printf("Cleared breakpoint %x (", BP_NUM(bp));
+               xmon_print_symbol(bp->address, " ", ")\n");
+               bp->enabled = 0;
                break;
-       case '?':
-               printf(breakpoint_help_string);
-               break;
+
        default:
                termch = cmd;
                cmd = skipbl();
@@ -816,53 +1259,30 @@ bpt_cmds(void)
                termch = cmd;
                if (!scanhex(&a)) {
                        /* print all breakpoints */
-                       int bpnum;
-
-                       printf("   type            address    count\n");
+                       printf("   type            address\n");
                        if (dabr.enabled) {
-                               printf("   data   %.16lx %8x [", dabr.address & ~7,
-                                      dabr.count);
-                               if (dabr.address & 1)
+                               printf("   data   %.16lx  [", dabr.address);
+                               if (dabr.enabled & 1)
                                        printf("r");
-                               if (dabr.address & 2)
+                               if (dabr.enabled & 2)
                                        printf("w");
                                printf("]\n");
                        }
-                       if (iabr.enabled)
-                               printf("   inst   %.16lx %8x\n", iabr.address & ~3,
-                                      iabr.count);
-                       for (bp = bpts, bpnum = 1; bp < &bpts[NBPTS]; ++bp, ++bpnum)
-                               if (bp->enabled) {
-                                       printf("%2x trap   %.16lx %8x  ",
-                                               bpnum, bp->address, bp->count);
-                                       xmon_print_symbol("%s\n", bp->address);
-                               }
-                       break;
-               }
-               
-               if (systemcfg->platform != PLATFORM_POWERMAC &&
-                  !(systemcfg->platform & PLATFORM_PSERIES)) {
-                       printf("Not supported for this platform\n");
-                       break;
-               }
-
-               bp = at_breakpoint(a);
-               if (bp == 0) {
-                       for (bp = bpts; bp < &bpts[NBPTS]; ++bp)
+                       for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
                                if (!bp->enabled)
-                                       break;
-                       if (bp >= &bpts[NBPTS]) {
-                               printf("Sorry, no free breakpoints.  Please clear one first.\n");
-                               break;
+                                       continue;
+                               printf("%2x %s   ", BP_NUM(bp),
+                                   (bp->enabled & BP_IABR)? "inst": "trap");
+                               xmon_print_symbol(bp->address, "  ", "\n");
                        }
+                       break;
                }
-               bp->enabled = 1;
-               bp->address = a;
-               bp->count = 0;
-               scanhex(&bp->count);
-               printf("Set breakpoint %2x trap   %.16lx %8x  ", (bp-bpts) + 1, 
-                       bp->address, bp->count);
-               xmon_print_symbol("%s\n", bp->address);
+
+               if (!check_bp_loc(a))
+                       break;
+               bp = new_breakpoint(a);
+               if (bp != NULL)
+                       bp->enabled |= BP_TRAP;
                break;
        }
 }
@@ -872,76 +1292,131 @@ 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 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 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 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;
 }
 
-/*
- * Most of our exceptions are in the form:
- *    bl handler
- *    b .ret_from_exception
- * and this currently fails to catch them.
- */
-static inline int exception_frame(unsigned long ip)
+static void get_function_bounds(unsigned long pc, unsigned long *startp,
+                               unsigned long *endp)
 {
-       extern void *ret_from_syscall_1, *ret_from_syscall_2, *ret_from_except;
-
-       if ((ip == (unsigned long)ret_from_syscall_1) ||
-           (ip == (unsigned long)ret_from_syscall_2) ||
-           (ip == (unsigned long)ret_from_except))
-               return 1;
+       unsigned long size, offset;
+       const char *name;
+       char *modname;
 
-       return 0;
+       *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;
 
-static void xmon_show_stack(unsigned long sp)
+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) {
-                       printf("SP in userspace\n");
+                       if (sp != 0)
+                               printf("SP (%lx) is in userspace\n", sp);
                        break;
                }
 
-               if (!mread((sp + 16), &ip, sizeof(unsigned long)))
+               if (!mread(sp + 16, &ip, sizeof(unsigned long))
+                   || !mread(sp, &newsp, sizeof(unsigned long))) {
+                       printf("Couldn't read stack frame at %lx\n", sp);
                        break;
+               }
 
-               printf("[%016lx] [%016lx] ", sp, ip);
-               xmon_print_symbol("%s\n", ip);
+               /*
+                * 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 + 16, &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("[%.16lx] ", sp);
+                               xmon_print_symbol(ip, " ", " (unreliable)\n");
+                       }
+                       pc = lr = 0;
 
-               if (exception_frame(ip)) {
-                       if (mread(sp+112, &regs, sizeof(regs)) != sizeof(regs))
-                               break;
+               } else {
+                       printf("[%.16lx] ", sp);
+                       xmon_print_symbol(ip, " ", "\n");
+               }
 
-                        printf("  exception: %lx %s regs %lx\n", regs.trap,
-                              getvecname(regs.trap), sp+112);
+               /* Look for "regshere" marker to see if this is
+                  an exception frame. */
+               if (mread(sp + 0x60, &marker, sizeof(unsigned long))
+                   && marker == 0x7265677368657265ul) {
+                       if (mread(sp + 0x70, &regs, sizeof(regs))
+                           != sizeof(regs)) {
+                               printf("Couldn't read registers at %lx\n",
+                                      sp + 0x70);
+                               break;
+                       }
+                        printf("--- Exception: %lx %s at ", regs.trap,
+                              getvecname(TRAP(&regs)));
+                       pc = regs.nip;
+                       lr = regs.link;
+                       xmon_print_symbol(pc, " ", "\n");
                }
 
-               if (!mread(sp, &newsp, sizeof(unsigned long)))
-                       break;
-               if (newsp < sp)
+               if (newsp == 0)
                        break;
 
                sp = newsp;
@@ -952,42 +1427,36 @@ static void backtrace(struct pt_regs *excp)
 {
        unsigned long sp;
 
-       if (excp == NULL)
-               sp = __get_SP();
+       if (scanhex(&sp))
+               xmon_show_stack(sp, 0, 0);
        else
-               sp = excp->gpr[1];
-
-       scanhex(&sp);
+               xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
        scannl();
-
-       xmon_show_stack(sp);
 }
 
-spinlock_t exception_print_lock = SPIN_LOCK_UNLOCKED;
-
 void excprint(struct pt_regs *fp)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&exception_print_lock, flags);
+       unsigned long trap;
 
 #ifdef CONFIG_SMP
-       printf("cpu %d: ", smp_processor_id());
+       printf("cpu 0x%x: ", smp_processor_id());
 #endif /* CONFIG_SMP */
 
-       printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(fp->trap), fp);
-       printf("    pc: %lx", fp->nip);
-       xmon_print_symbol(" (%s)\n", fp->nip);
+       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: %lx", fp->link);
-       xmon_print_symbol(" (%s)\n", fp->link);
+       printf("    lr: ", fp->link);
+       xmon_print_symbol(fp->link, ": ", "\n");
 
        printf("    sp: %lx\n", fp->gpr[1]);
        printf("   msr: %lx\n", fp->msr);
 
-       if (fp->trap == 0x300 || fp->trap == 0x380 || fp->trap == 0x600) {
+       if (trap == 0x300 || trap == 0x380 || trap == 0x600) {
                printf("   dar: %lx\n", fp->dar);
-               printf(" dsisr: %lx\n", fp->dsisr);
+               if (trap != 0x380)
+                       printf(" dsisr: %lx\n", fp->dsisr);
        }
 
        printf("  current = 0x%lx\n", current);
@@ -996,39 +1465,50 @@ void excprint(struct pt_regs *fp)
                printf("    pid   = %ld, comm = %s\n",
                       current->pid, current->comm);
        }
-
-       spin_unlock_irqrestore(&exception_print_lock, flags);
 }
 
 void prregs(struct pt_regs *fp)
 {
        int n;
        unsigned long base;
+       struct pt_regs regs;
 
-       if (scanhex((void *)&base))
-               fp = (struct pt_regs *) base;
+       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 %.16lx\n",
+                              base);
+                       return;
+               }
+               catch_memory_errors = 0;
+               fp = &regs;
+       }
 
-       if (setjmp(bus_error_jmp) == 0) {
-               __debugger_fault_handler = handle_fault;
-               sync();
+       if (FULL_REGS(fp)) {
                for (n = 0; n < 16; ++n)
-                       printf("R%.2ld = %.16lx   R%.2ld = %.16lx\n", n,
-                              fp->gpr[n], n+16, fp->gpr[n+16]);
-               printf("pc  = %.16lx   msr = %.16lx\nlr  = %.16lx   "
-                      "cr  = %.16lx\n", fp->nip, fp->msr, fp->link, fp->ccr);
-               printf("ctr = %.16lx   xer = %.16lx   trap = %8lx\n",
-                      fp->ctr, fp->xer, fp->trap);
-
-               sync();
-               /* wait a little while to see if we get a machine check */
-               __delay(200);
+                       printf("R%.2ld = %.16lx   R%.2ld = %.16lx\n",
+                              n, fp->gpr[n], n+16, fp->gpr[n+16]);
        } else {
-               printf("*** Error reading regs\n");
+               for (n = 0; n < 7; ++n)
+                       printf("R%.2ld = %.16lx   R%.2ld = %.16lx\n",
+                              n, fp->gpr[n], n+7, fp->gpr[n+7]);
        }
+       printf("pc  = ");
+       xmon_print_symbol(fp->nip, " ", "\n");
+       printf("lr  = ");
+       xmon_print_symbol(fp->link, " ", "\n");
+       printf("msr = %.16lx   cr  = %.8lx\n", fp->msr, fp->ccr);
+       printf("ctr = %.16lx   xer = %.16lx   trap = %8lx\n",
+              fp->ctr, fp->xer, fp->trap);
 }
 
-void
-cacheflush(void)
+void cacheflush(void)
 {
        int cmd;
        unsigned long nflush;
@@ -1043,7 +1523,7 @@ cacheflush(void)
        scanhex(&nflush);
        nflush = (nflush + L1_CACHE_BYTES - 1) / L1_CACHE_BYTES;
        if (setjmp(bus_error_jmp) == 0) {
-               __debugger_fault_handler = handle_fault;
+               catch_memory_errors = 1;
                sync();
 
                if (cmd != 'i') {
@@ -1057,7 +1537,7 @@ cacheflush(void)
                /* wait a little while to see if we get a machine check */
                __delay(200);
        }
-       __debugger_fault_handler = 0;
+       catch_memory_errors = 0;
 }
 
 unsigned long
@@ -1077,20 +1557,7 @@ read_spr(int n)
        store_inst(instrs+1);
        code = (unsigned long (*)(void)) opd;
 
-       if (setjmp(bus_error_jmp) == 0) {
-               __debugger_fault_handler = handle_fault;
-               sync();
-
-               ret = code();
-
-               sync();
-               /* wait a little while to see if we get a machine check */
-               __delay(200);
-       } else {
-               printf("*** Error reading spr %x\n", n);
-       }
-
-       __debugger_fault_handler = 0;
+       ret = code();
 
        return ret;
 }
@@ -1111,20 +1578,7 @@ write_spr(int n, unsigned long val)
        store_inst(instrs+1);
        code = (unsigned long (*)(unsigned long)) opd;
 
-       if (setjmp(bus_error_jmp) == 0) {
-               __debugger_fault_handler = handle_fault;
-               sync();
-
-               code(val);
-
-               sync();
-               /* wait a little while to see if we get a machine check */
-               __delay(200);
-       } else {
-               printf("*** Error writing spr %x\n", n);
-       }
-
-       __debugger_fault_handler = 0;
+       code(val);
 }
 
 static unsigned long regno;
@@ -1161,15 +1615,20 @@ super_regs()
     
                printf("  Local Processor Control Area (LpPaca): \n");
                ptrLpPaca = ptrPaca->xLpPacaPtr;
-               printf("    Saved Srr0=%.16lx  Saved Srr1=%.16lx \n", ptrLpPaca->xSavedSrr0, ptrLpPaca->xSavedSrr1);
-               printf("    Saved Gpr3=%.16lx  Saved Gpr4=%.16lx \n", ptrLpPaca->xSavedGpr3, ptrLpPaca->xSavedGpr4);
+               printf("    Saved Srr0=%.16lx  Saved Srr1=%.16lx \n",
+                      ptrLpPaca->xSavedSrr0, ptrLpPaca->xSavedSrr1);
+               printf("    Saved Gpr3=%.16lx  Saved Gpr4=%.16lx \n",
+                      ptrLpPaca->xSavedGpr3, ptrLpPaca->xSavedGpr4);
                printf("    Saved Gpr5=%.16lx \n", ptrLpPaca->xSavedGpr5);
     
                printf("  Local Processor Register Save Area (LpRegSave): \n");
                ptrLpRegSave = ptrPaca->xLpRegSavePtr;
-               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);
+               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;
@@ -1194,6 +1653,9 @@ super_regs()
        scannl();
 }
 
+/*
+ * Stuff for reading and writing memory safely
+ */
 int
 mread(unsigned long adrs, void *buf, int size)
 {
@@ -1202,7 +1664,7 @@ mread(unsigned long adrs, void *buf, int size)
 
        n = 0;
        if (setjmp(bus_error_jmp) == 0) {
-               __debugger_fault_handler = handle_fault;
+               catch_memory_errors = 1;
                sync();
                p = (char *)adrs;
                q = (char *)buf;
@@ -1227,7 +1689,7 @@ mread(unsigned long adrs, void *buf, int size)
                __delay(200);
                n = size;
        }
-       __debugger_fault_handler = 0;
+       catch_memory_errors = 0;
        return n;
 }
 
@@ -1239,7 +1701,7 @@ mwrite(unsigned long adrs, void *buf, int size)
 
        n = 0;
        if (setjmp(bus_error_jmp) == 0) {
-               __debugger_fault_handler = handle_fault;
+               catch_memory_errors = 1;
                sync();
                p = (char *) adrs;
                q = (char *) buf;
@@ -1266,7 +1728,7 @@ mwrite(unsigned long adrs, void *buf, int size)
        } else {
                printf("*** Error writing address %x\n", adrs + n);
        }
-       __debugger_fault_handler = 0;
+       catch_memory_errors = 0;
        return n;
 }
 
@@ -1276,7 +1738,7 @@ static char *fault_chars[] = { "--", "**", "##" };
 static int
 handle_fault(struct pt_regs *regs)
 {
-       switch (regs->trap) {
+       switch (TRAP(regs)) {
        case 0x200:
                fault_type = 0;
                break;
@@ -1541,7 +2003,7 @@ dump()
                scanhex(&nidump);
                if( nidump == 0 )
                        nidump = 16;
-               adrs += ppc_inst_dump(adrs, nidump);
+               adrs += ppc_inst_dump(adrs, nidump, 1);
                last_cmd = "di\n";
        } else {
                scanhex(&ndump);
@@ -1559,41 +2021,43 @@ prdump(unsigned long adrs, long ndump)
        long n, m, c, r, nr;
        unsigned char temp[16];
 
-       for( n = ndump; n > 0; ){
+       for (n = ndump; n > 0;) {
                printf("%.16lx", adrs);
                putchar(' ');
                r = n < 16? n: 16;
                nr = mread(adrs, temp, r);
                adrs += nr;
-               for( m = 0; m < r; ++m ){
+               for (m = 0; m < r; ++m) {
                        if ((m & 7) == 0 && m > 0)
                            putchar(' ');
-                       if( m < nr )
+                       if (m < nr)
                                printf("%.2x", temp[m]);
                        else
                                printf("%s", fault_chars[fault_type]);
                }
-               for(; m < 16; ++m )
-                       printf("   ");
+               if (m <= 8)
+                       printf(" ");
+               for (; m < 16; ++m)
+                       printf("  ");
                printf("  |");
-               for( m = 0; m < r; ++m ){
-                       if( m < nr ){
+               for (m = 0; m < r; ++m) {
+                       if (m < nr) {
                                c = temp[m];
                                putchar(' ' <= c && c <= '~'? c: '.');
                        } else
                                putchar(' ');
                }
                n -= r;
-               for(; m < 16; ++m )
+               for (; m < 16; ++m)
                        putchar(' ');
                printf("|\n");
-               if( nr < r )
+               if (nr < r)
                        break;
        }
 }
 
 int
-ppc_inst_dump(unsigned long adr, long count)
+ppc_inst_dump(unsigned long adr, long count, int praddr)
 {
        int nr, dotted;
        unsigned long first_adr;
@@ -1601,11 +2065,13 @@ ppc_inst_dump(unsigned long adr, long count)
        unsigned char val[4];
 
        dotted = 0;
-       for (first_adr = adr; count > 0; --count, adr += 4){
+       for (first_adr = adr; count > 0; --count, adr += 4) {
                nr = mread(adr, val, 4);
-               if( nr == 0 ){
-                       const char *x = fault_chars[fault_type];
-                       printf("%.16lx  %s%s%s%s\n", adr, x, x, x, x);
+               if (nr == 0) {
+                       if (praddr) {
+                               const char *x = fault_chars[fault_type];
+                               printf("%.16lx  %s%s%s%s\n", adr, x, x, x, x);
+                       }
                        break;
                }
                inst = GETWORD(val);
@@ -1618,9 +2084,10 @@ ppc_inst_dump(unsigned long adr, long count)
                }
                dotted = 0;
                last_inst = inst;
-               printf("%.16lx  ", adr);
-               printf("%.8x\t", inst);
-               print_insn_big_powerpc(stdout, inst, adr);      /* always returns 4 */
+               if (praddr)
+                       printf("%.16lx  %.8x", adr, inst);
+               printf("\t");
+               print_insn_powerpc(inst, adr, 0);       /* always returns 4 */
                printf("\n");
        }
        return adr - first_adr;
@@ -1629,19 +2096,7 @@ ppc_inst_dump(unsigned long adr, long count)
 void
 print_address(unsigned long addr)
 {
-       const char *name;
-       char *modname;
-       long size, offset;
-
-       name = kallsyms_lookup(addr, &size, &offset, &modname, tmpstr);
-
-       if (name) {
-               if (modname)
-                       printf("0x%lx\t# %s:%s+0x%lx", addr, modname, name, offset);
-               else
-                       printf("0x%lx\t# %s+0x%lx", addr, name, offset);
-       } else
-               printf("0x%lx", addr);
+       xmon_print_symbol(addr, "\t# ", "");
 }
 
 
@@ -1781,7 +2236,7 @@ static char *regnames[N_PTREGS] = {
        "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", "mq",
+       "pc", "msr", "or3", "ctr", "lr", "xer", "ccr", "softe",
        "trap", "dar", "dsisr", "res"
 };
 
@@ -1809,13 +2264,11 @@ unsigned long *vp;
                regname[i] = 0;
                for (i = 0; i < N_PTREGS; ++i) {
                        if (strcmp(regnames[i], regname) == 0) {
-                               unsigned long *rp = (unsigned long *)
-                                       xmon_regs[smp_processor_id()];
-                               if (rp == NULL) {
+                               if (xmon_regs == NULL) {
                                        printf("regs not available\n");
                                        return 0;
                                }
-                               *vp = rp[i];
+                               *vp = ((unsigned long *)xmon_regs)[i];
                                return 1;
                        }
                }
@@ -1827,8 +2280,16 @@ unsigned long *vp;
 
        if (c == '0') {
                c = inchar();
-               if (c == 'x')
+               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++) {
@@ -1943,16 +2404,14 @@ symbol_lookup(void)
 
        switch (type) {
        case 'a':
-               if (scanhex(&addr)) {
-                       printf("%lx: ", addr);
-                       xmon_print_symbol("%s\n", addr);
-               }
+               if (scanhex(&addr))
+                       xmon_print_symbol(addr, ": ", "\n");
                termch = 0;
                break;
        case 's':
                getstring(tmp, 64);
                if (setjmp(bus_error_jmp) == 0) {
-                       __debugger_fault_handler = handle_fault;
+                       catch_memory_errors = 1;
                        sync();
                        addr = kallsyms_lookup_name(tmp);
                        if (addr)
@@ -1961,58 +2420,40 @@ symbol_lookup(void)
                                printf("Symbol '%s' not found.\n", tmp);
                        sync();
                }
-               __debugger_fault_handler = 0;
+               catch_memory_errors = 0;
                termch = 0;
                break;
        }
 }
 
 
-/* xmon version of __print_symbol */
-void __xmon_print_symbol(const char *fmt, unsigned long address)
+/* 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;
+       const char *name = NULL;
        unsigned long offset, size;
 
+       printf("%.16lx", address);
        if (setjmp(bus_error_jmp) == 0) {
-               __debugger_fault_handler = handle_fault;
+               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);
-       } else {
-               name = "symbol lookup failed";
-       }
-
-       __debugger_fault_handler = 0;
-
-       if (!name) {
-               char addrstr[sizeof("0x%lx") + (BITS_PER_LONG*3/10)];
-
-               sprintf(addrstr, "0x%lx", address);
-               printf(fmt, addrstr);
-               return;
        }
 
-       if (modname) {
-               /* This is pretty small. */
-               char buffer[sizeof("%s+%#lx/%#lx [%s]")
-                          + strlen(name) + 2*(BITS_PER_LONG*3/10)
-                          + strlen(modname)];
-
-               sprintf(buffer, "%s+%#lx/%#lx [%s]",
-                       name, offset, size, modname);
-               printf(fmt, buffer);
-       } else {
-               char buffer[sizeof("%s+%#lx/%#lx")
-                          + strlen(name) + 2*(BITS_PER_LONG*3/10)];
+       catch_memory_errors = 0;
 
-               sprintf(buffer, "%s+%#lx/%#lx", name, offset, size);
-               printf(fmt, buffer);
+       if (name) {
+               printf("%s%s+%#lx/%#lx", mid, name, offset, size);
+               if (modname)
+                       printf(" [%s]", modname);
        }
+       printf("%s", after);
 }
 
 static void debug_trace(void)
@@ -2069,9 +2510,9 @@ static void dump_slb(void)
        int i;
        unsigned long tmp;
 
-       printf("SLB contents of cpu %d\n", smp_processor_id());
+       printf("SLB contents of cpu %x\n", smp_processor_id());
 
-       for (i = 0; i < naca->slb_size; i++) {
+       for (i = 0; i < SLB_NUM_ENTRIES; i++) {
                asm volatile("slbmfee  %0,%1" : "=r" (tmp) : "r" (i));
                printf("%02d %016lx ", i, tmp);
 
@@ -2085,7 +2526,7 @@ static void dump_stab(void)
        int i;
        unsigned long *tmp = (unsigned long *)get_paca()->xStab_data.virt;
 
-       printf("Segment table contents of cpu %d\n", smp_processor_id());
+       printf("Segment table contents of cpu %x\n", smp_processor_id());
 
        for (i = 0; i < PAGE_SIZE/16; i++) {
                unsigned long a, b;
@@ -2103,10 +2544,12 @@ static void dump_stab(void)
 void xmon_init(void)
 {
        __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;
 }
 
 void dump_segments(void)