blob: 076368c8b8a95e09f7c5f7cdd1cbf69a012d883c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Routines providing a simple monitor for use on the PowerMac.
3 *
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11004 * Copyright (C) 1996-2005 Paul Mackerras.
Michael Ellerman476792832006-10-03 14:12:08 +10005 * Copyright (C) 2001 PPC64 Team, IBM Corp
6 * Copyrignt (C) 2006 Michael Ellerman, IBM Corp
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/errno.h>
14#include <linux/sched.h>
15#include <linux/smp.h>
16#include <linux/mm.h>
17#include <linux/reboot.h>
18#include <linux/delay.h>
19#include <linux/kallsyms.h>
20#include <linux/cpumask.h>
Paul Mackerrasf78541d2005-10-28 22:53:37 +100021#include <linux/module.h>
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +110022#include <linux/sysrq.h>
Andrew Morton4694ca02005-11-13 16:06:50 -080023#include <linux/interrupt.h>
David Howells7d12e782006-10-05 14:55:46 +010024#include <linux/irq.h>
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -080025#include <linux/bug.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27#include <asm/ptrace.h>
28#include <asm/string.h>
29#include <asm/prom.h>
30#include <asm/machdep.h>
Paul Mackerrasf78541d2005-10-28 22:53:37 +100031#include <asm/xmon.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <asm/processor.h>
33#include <asm/pgtable.h>
34#include <asm/mmu.h>
35#include <asm/mmu_context.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <asm/cputable.h>
37#include <asm/rtas.h>
38#include <asm/sstep.h>
Paul Mackerrasf583ffc2006-10-10 11:47:07 +100039#include <asm/irq_regs.h>
Michael Ellermanff8a8f22006-10-24 18:31:27 +020040#include <asm/spu.h>
41#include <asm/spu_priv1.h>
Stephen Rothwell1d135812006-11-13 14:50:28 +110042#include <asm/firmware.h>
Michael Neulingc3b75bd2008-01-18 15:50:30 +110043#include <asm/setjmp.h>
Paul Mackerrasf78541d2005-10-28 22:53:37 +100044
45#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <asm/hvcall.h>
Paul Mackerrasf78541d2005-10-28 22:53:37 +100047#include <asm/paca.h>
48#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
50#include "nonstdio.h"
Michael Ellermane0426042006-11-23 00:46:45 +010051#include "dis-asm.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53#define scanhex xmon_scanhex
54#define skipbl xmon_skipbl
55
56#ifdef CONFIG_SMP
Michael Ellerman1c8950f2008-05-08 14:27:17 +100057static cpumask_t cpus_in_xmon = CPU_MASK_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058static unsigned long xmon_taken = 1;
59static int xmon_owner;
60static int xmon_gate;
61#endif /* CONFIG_SMP */
62
63static unsigned long in_xmon = 0;
64
65static unsigned long adrs;
66static int size = 1;
67#define MAX_DUMP (128 * 1024)
68static unsigned long ndump = 64;
69static unsigned long nidump = 16;
70static unsigned long ncsum = 4096;
71static int termch;
72static char tmpstr[128];
73
Linus Torvalds1da177e2005-04-16 15:20:36 -070074static long bus_error_jmp[JMP_BUF_LEN];
75static int catch_memory_errors;
76static long *xmon_fault_jmp[NR_CPUS];
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78/* Breakpoint stuff */
79struct bpt {
80 unsigned long address;
81 unsigned int instr[2];
82 atomic_t ref_count;
83 int enabled;
84 unsigned long pad;
85};
86
87/* Bits in bpt.enabled */
88#define BP_IABR_TE 1 /* IABR translation enabled */
89#define BP_IABR 2
90#define BP_TRAP 8
91#define BP_DABR 0x10
92
93#define NBPTS 256
94static struct bpt bpts[NBPTS];
95static struct bpt dabr;
96static struct bpt *iabr;
97static unsigned bpinstr = 0x7fe00008; /* trap */
98
99#define BP_NUM(bp) ((bp) - bpts + 1)
100
101/* Prototypes */
102static int cmds(struct pt_regs *);
103static int mread(unsigned long, void *, int);
104static int mwrite(unsigned long, void *, int);
105static int handle_fault(struct pt_regs *);
106static void byterev(unsigned char *, int);
107static void memex(void);
108static int bsesc(void);
109static void dump(void);
110static void prdump(unsigned long, long);
111static int ppc_inst_dump(unsigned long, long, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112static void backtrace(struct pt_regs *);
113static void excprint(struct pt_regs *);
114static void prregs(struct pt_regs *);
115static void memops(int);
116static void memlocate(void);
117static void memzcan(void);
118static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned);
119int skipbl(void);
120int scanhex(unsigned long *valp);
121static void scannl(void);
122static int hexdigit(int);
123void getstring(char *, int);
124static void flush_input(void);
125static int inchar(void);
126static void take_input(char *);
127static unsigned long read_spr(int);
128static void write_spr(int, unsigned long);
129static void super_regs(void);
130static void remove_bpts(void);
131static void insert_bpts(void);
132static void remove_cpu_bpts(void);
133static void insert_cpu_bpts(void);
134static struct bpt *at_breakpoint(unsigned long pc);
135static struct bpt *in_breakpoint_table(unsigned long pc, unsigned long *offp);
136static int do_step(struct pt_regs *);
137static void bpt_cmds(void);
138static void cacheflush(void);
139static int cpu_cmd(void);
140static void csum(void);
141static void bootcmds(void);
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000142static void proccall(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143void dump_segments(void);
144static void symbol_lookup(void);
Olaf Hering26c8af52006-09-08 16:29:21 +0200145static void xmon_show_stack(unsigned long sp, unsigned long lr,
146 unsigned long pc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147static void xmon_print_symbol(unsigned long address, const char *mid,
148 const char *after);
149static const char *getvecname(unsigned long vec);
150
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200151static int do_spu_cmd(void);
152
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100153#ifdef CONFIG_44x
154static void dump_tlb_44x(void);
155#endif
156
Michael Ellerman9f1067c22008-05-08 14:27:16 +1000157static int xmon_no_auto_backtrace;
Olaf Hering26c8af52006-09-08 16:29:21 +0200158
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000159extern void xmon_enter(void);
160extern void xmon_leave(void);
161
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000162extern void xmon_save_regs(struct pt_regs *);
163
164#ifdef CONFIG_PPC64
165#define REG "%.16lx"
166#define REGS_PER_LINE 4
167#define LAST_VOLATILE 13
168#else
169#define REG "%.8lx"
170#define REGS_PER_LINE 8
171#define LAST_VOLATILE 12
172#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
174#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])
175
176#define isxdigit(c) (('0' <= (c) && (c) <= '9') \
177 || ('a' <= (c) && (c) <= 'f') \
178 || ('A' <= (c) && (c) <= 'F'))
179#define isalnum(c) (('0' <= (c) && (c) <= '9') \
180 || ('a' <= (c) && (c) <= 'z') \
181 || ('A' <= (c) && (c) <= 'Z'))
182#define isspace(c) (c == ' ' || c == '\t' || c == 10 || c == 13 || c == 0)
183
184static char *help_string = "\
185Commands:\n\
186 b show breakpoints\n\
187 bd set data breakpoint\n\
188 bi set instruction breakpoint\n\
189 bc clear breakpoint\n"
190#ifdef CONFIG_SMP
191 "\
192 c print cpus stopped in xmon\n\
193 c# try to switch to cpu number h (in hex)\n"
194#endif
195 "\
196 C checksum\n\
197 d dump bytes\n\
198 di dump instructions\n\
199 df dump float values\n\
200 dd dump double values\n\
Olaf Hering7e5b5932006-03-08 20:40:28 +0100201 dr dump stream of raw bytes\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 e print exception information\n\
203 f flush cache\n\
204 la lookup symbol+offset of specified address\n\
205 ls lookup address of specified symbol\n\
206 m examine/change memory\n\
207 mm move a block of memory\n\
208 ms set a block of memory\n\
209 md compare two blocks of memory\n\
210 ml locate a block of memory\n\
211 mz zero a block of memory\n\
212 mi show information about memory allocation\n\
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000213 p call a procedure\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 r print registers\n\
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200215 s single step\n"
Arnd Bergmanne0555952006-11-27 19:18:55 +0100216#ifdef CONFIG_SPU_BASE
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200217" ss stop execution on all spus\n\
Michael Ellermana8984972006-10-24 18:31:28 +0200218 sr restore execution on stopped spus\n\
Michael Ellerman24a24c82006-11-23 00:46:41 +0100219 sf # dump spu fields for spu # (in hex)\n\
Michael Ellermanc99176a2007-02-26 18:14:06 +0900220 sd # dump spu local store for spu # (in hex)\n\
Michael Ellermanaf89fb82006-11-23 00:46:44 +0100221 sdi # disassemble spu local store for spu # (in hex)\n"
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200222#endif
223" S print special registers\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 t print backtrace\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 x exit monitor and recover\n\
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000226 X exit monitor and dont recover\n"
227#ifdef CONFIG_PPC64
228" u dump segment table or SLB\n"
229#endif
230#ifdef CONFIG_PPC_STD_MMU_32
231" u dump segment registers\n"
232#endif
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100233#ifdef CONFIG_44x
234" u dump TLB\n"
235#endif
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000236" ? help\n"
237" zr reboot\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 zh halt\n"
239;
240
241static struct pt_regs *xmon_regs;
242
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000243static inline void sync(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244{
245 asm volatile("sync; isync");
246}
247
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000248static inline void store_inst(void *p)
249{
250 asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p));
251}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000253static inline void cflush(void *p)
254{
255 asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p));
256}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000258static inline void cinval(void *p)
259{
260 asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p));
261}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263/*
264 * Disable surveillance (the service processor watchdog function)
265 * while we are in xmon.
266 * XXX we should re-enable it when we leave. :)
267 */
268#define SURVEILLANCE_TOKEN 9000
269
270static inline void disable_surveillance(void)
271{
272#ifdef CONFIG_PPC_PSERIES
273 /* Since this can't be a module, args should end up below 4GB. */
274 static struct rtas_args args;
275
276 /*
277 * At this point we have got all the cpus we can into
278 * xmon, so there is hopefully no other cpu calling RTAS
279 * at the moment, even though we don't take rtas.lock.
280 * If we did try to take rtas.lock there would be a
281 * real possibility of deadlock.
282 */
283 args.token = rtas_token("set-indicator");
284 if (args.token == RTAS_UNKNOWN_SERVICE)
285 return;
286 args.nargs = 3;
287 args.nret = 1;
288 args.rets = &args.args[3];
289 args.args[0] = SURVEILLANCE_TOKEN;
290 args.args[1] = 0;
291 args.args[2] = 0;
292 enter_rtas(__pa(&args));
293#endif /* CONFIG_PPC_PSERIES */
294}
295
296#ifdef CONFIG_SMP
297static int xmon_speaker;
298
299static void get_output_lock(void)
300{
301 int me = smp_processor_id() + 0x100;
302 int last_speaker = 0, prev;
303 long timeout;
304
305 if (xmon_speaker == me)
306 return;
307 for (;;) {
308 if (xmon_speaker == 0) {
309 last_speaker = cmpxchg(&xmon_speaker, 0, me);
310 if (last_speaker == 0)
311 return;
312 }
313 timeout = 10000000;
314 while (xmon_speaker == last_speaker) {
315 if (--timeout > 0)
316 continue;
317 /* hostile takeover */
318 prev = cmpxchg(&xmon_speaker, last_speaker, me);
319 if (prev == last_speaker)
320 return;
321 break;
322 }
323 }
324}
325
326static void release_output_lock(void)
327{
328 xmon_speaker = 0;
329}
Michael Ellerman1c8950f2008-05-08 14:27:17 +1000330
331int cpus_are_in_xmon(void)
332{
333 return !cpus_empty(cpus_in_xmon);
334}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335#endif
336
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000337static int xmon_core(struct pt_regs *regs, int fromipi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338{
339 int cmd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 struct bpt *bp;
341 long recurse_jmp[JMP_BUF_LEN];
342 unsigned long offset;
Anton Blanchardf13659e2007-03-21 01:48:34 +1100343 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344#ifdef CONFIG_SMP
345 int cpu;
346 int secondary;
347 unsigned long timeout;
348#endif
349
Anton Blanchardf13659e2007-03-21 01:48:34 +1100350 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
352 bp = in_breakpoint_table(regs->nip, &offset);
353 if (bp != NULL) {
354 regs->nip = bp->address + offset;
355 atomic_dec(&bp->ref_count);
356 }
357
358 remove_cpu_bpts();
359
360#ifdef CONFIG_SMP
361 cpu = smp_processor_id();
362 if (cpu_isset(cpu, cpus_in_xmon)) {
363 get_output_lock();
364 excprint(regs);
365 printf("cpu 0x%x: Exception %lx %s in xmon, "
366 "returning to main loop\n",
367 cpu, regs->trap, getvecname(TRAP(regs)));
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000368 release_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 longjmp(xmon_fault_jmp[cpu], 1);
370 }
371
372 if (setjmp(recurse_jmp) != 0) {
373 if (!in_xmon || !xmon_gate) {
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000374 get_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 printf("xmon: WARNING: bad recursive fault "
376 "on cpu 0x%x\n", cpu);
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000377 release_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 goto waiting;
379 }
380 secondary = !(xmon_taken && cpu == xmon_owner);
381 goto cmdloop;
382 }
383
384 xmon_fault_jmp[cpu] = recurse_jmp;
385 cpu_set(cpu, cpus_in_xmon);
386
387 bp = NULL;
388 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF))
389 bp = at_breakpoint(regs->nip);
390 if (bp || (regs->msr & MSR_RI) == 0)
391 fromipi = 0;
392
393 if (!fromipi) {
394 get_output_lock();
395 excprint(regs);
396 if (bp) {
397 printf("cpu 0x%x stopped at breakpoint 0x%x (",
398 cpu, BP_NUM(bp));
399 xmon_print_symbol(regs->nip, " ", ")\n");
400 }
401 if ((regs->msr & MSR_RI) == 0)
402 printf("WARNING: exception is not recoverable, "
403 "can't continue\n");
404 release_output_lock();
405 }
406
407 waiting:
408 secondary = 1;
409 while (secondary && !xmon_gate) {
410 if (in_xmon == 0) {
411 if (fromipi)
412 goto leave;
413 secondary = test_and_set_bit(0, &in_xmon);
414 }
415 barrier();
416 }
417
418 if (!secondary && !xmon_gate) {
419 /* we are the first cpu to come in */
420 /* interrupt other cpu(s) */
421 int ncpus = num_online_cpus();
422
423 xmon_owner = cpu;
424 mb();
425 if (ncpus > 1) {
426 smp_send_debugger_break(MSG_ALL_BUT_SELF);
427 /* wait for other cpus to come in */
428 for (timeout = 100000000; timeout != 0; --timeout) {
429 if (cpus_weight(cpus_in_xmon) >= ncpus)
430 break;
431 barrier();
432 }
433 }
434 remove_bpts();
435 disable_surveillance();
436 /* for breakpoint or single step, print the current instr. */
437 if (bp || TRAP(regs) == 0xd00)
438 ppc_inst_dump(regs->nip, 1, 0);
439 printf("enter ? for help\n");
440 mb();
441 xmon_gate = 1;
442 barrier();
443 }
444
445 cmdloop:
446 while (in_xmon) {
447 if (secondary) {
448 if (cpu == xmon_owner) {
449 if (!test_and_set_bit(0, &xmon_taken)) {
450 secondary = 0;
451 continue;
452 }
453 /* missed it */
454 while (cpu == xmon_owner)
455 barrier();
456 }
457 barrier();
458 } else {
459 cmd = cmds(regs);
460 if (cmd != 0) {
461 /* exiting xmon */
462 insert_bpts();
463 xmon_gate = 0;
464 wmb();
465 in_xmon = 0;
466 break;
467 }
468 /* have switched to some other cpu */
469 secondary = 1;
470 }
471 }
472 leave:
473 cpu_clear(cpu, cpus_in_xmon);
474 xmon_fault_jmp[cpu] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475#else
476 /* UP is simple... */
477 if (in_xmon) {
478 printf("Exception %lx %s in xmon, returning to main loop\n",
479 regs->trap, getvecname(TRAP(regs)));
480 longjmp(xmon_fault_jmp[0], 1);
481 }
482 if (setjmp(recurse_jmp) == 0) {
483 xmon_fault_jmp[0] = recurse_jmp;
484 in_xmon = 1;
485
486 excprint(regs);
487 bp = at_breakpoint(regs->nip);
488 if (bp) {
489 printf("Stopped at breakpoint %x (", BP_NUM(bp));
490 xmon_print_symbol(regs->nip, " ", ")\n");
491 }
492 if ((regs->msr & MSR_RI) == 0)
493 printf("WARNING: exception is not recoverable, "
494 "can't continue\n");
495 remove_bpts();
496 disable_surveillance();
497 /* for breakpoint or single step, print the current instr. */
498 if (bp || TRAP(regs) == 0xd00)
499 ppc_inst_dump(regs->nip, 1, 0);
500 printf("enter ? for help\n");
501 }
502
503 cmd = cmds(regs);
504
505 insert_bpts();
506 in_xmon = 0;
507#endif
508
509 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
510 bp = at_breakpoint(regs->nip);
511 if (bp != NULL) {
512 int stepped = emulate_step(regs, bp->instr[0]);
513 if (stepped == 0) {
514 regs->nip = (unsigned long) &bp->instr[0];
515 atomic_inc(&bp->ref_count);
516 } else if (stepped < 0) {
517 printf("Couldn't single-step %s instruction\n",
518 (IS_RFID(bp->instr[0])? "rfid": "mtmsrd"));
519 }
520 }
521 }
522
523 insert_cpu_bpts();
524
Anton Blanchardf13659e2007-03-21 01:48:34 +1100525 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
Paul Mackerras0a730ae2006-10-03 21:32:49 +1000527 return cmd != 'X' && cmd != EOF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528}
529
530int xmon(struct pt_regs *excp)
531{
532 struct pt_regs regs;
533
534 if (excp == NULL) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000535 xmon_save_regs(&regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 excp = &regs;
537 }
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200538
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 return xmon_core(excp, 0);
540}
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000541EXPORT_SYMBOL(xmon);
542
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000543irqreturn_t xmon_irq(int irq, void *d)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000544{
545 unsigned long flags;
546 local_irq_save(flags);
547 printf("Keyboard interrupt\n");
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000548 xmon(get_irq_regs());
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000549 local_irq_restore(flags);
550 return IRQ_HANDLED;
551}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000553static int xmon_bpt(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554{
555 struct bpt *bp;
556 unsigned long offset;
557
558 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
559 return 0;
560
561 /* Are we at the trap at bp->instr[1] for some bp? */
562 bp = in_breakpoint_table(regs->nip, &offset);
563 if (bp != NULL && offset == 4) {
564 regs->nip = bp->address + 4;
565 atomic_dec(&bp->ref_count);
566 return 1;
567 }
568
569 /* Are we at a breakpoint? */
570 bp = at_breakpoint(regs->nip);
571 if (!bp)
572 return 0;
573
574 xmon_core(regs, 0);
575
576 return 1;
577}
578
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000579static int xmon_sstep(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580{
581 if (user_mode(regs))
582 return 0;
583 xmon_core(regs, 0);
584 return 1;
585}
586
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000587static int xmon_dabr_match(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588{
589 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
590 return 0;
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000591 if (dabr.enabled == 0)
592 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 xmon_core(regs, 0);
594 return 1;
595}
596
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000597static int xmon_iabr_match(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598{
599 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
600 return 0;
Michael Ellerman9f1067c22008-05-08 14:27:16 +1000601 if (iabr == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 return 0;
603 xmon_core(regs, 0);
604 return 1;
605}
606
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000607static int xmon_ipi(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608{
609#ifdef CONFIG_SMP
610 if (in_xmon && !cpu_isset(smp_processor_id(), cpus_in_xmon))
611 xmon_core(regs, 1);
612#endif
613 return 0;
614}
615
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000616static int xmon_fault_handler(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617{
618 struct bpt *bp;
619 unsigned long offset;
620
621 if (in_xmon && catch_memory_errors)
622 handle_fault(regs); /* doesn't return */
623
624 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
625 bp = in_breakpoint_table(regs->nip, &offset);
626 if (bp != NULL) {
627 regs->nip = bp->address + offset;
628 atomic_dec(&bp->ref_count);
629 }
630 }
631
632 return 0;
633}
634
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635static struct bpt *at_breakpoint(unsigned long pc)
636{
637 int i;
638 struct bpt *bp;
639
640 bp = bpts;
641 for (i = 0; i < NBPTS; ++i, ++bp)
642 if (bp->enabled && pc == bp->address)
643 return bp;
644 return NULL;
645}
646
647static struct bpt *in_breakpoint_table(unsigned long nip, unsigned long *offp)
648{
649 unsigned long off;
650
651 off = nip - (unsigned long) bpts;
652 if (off >= sizeof(bpts))
653 return NULL;
654 off %= sizeof(struct bpt);
655 if (off != offsetof(struct bpt, instr[0])
656 && off != offsetof(struct bpt, instr[1]))
657 return NULL;
658 *offp = off - offsetof(struct bpt, instr[0]);
659 return (struct bpt *) (nip - off);
660}
661
662static struct bpt *new_breakpoint(unsigned long a)
663{
664 struct bpt *bp;
665
666 a &= ~3UL;
667 bp = at_breakpoint(a);
668 if (bp)
669 return bp;
670
671 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
672 if (!bp->enabled && atomic_read(&bp->ref_count) == 0) {
673 bp->address = a;
674 bp->instr[1] = bpinstr;
675 store_inst(&bp->instr[1]);
676 return bp;
677 }
678 }
679
680 printf("Sorry, no free breakpoints. Please clear one first.\n");
681 return NULL;
682}
683
684static void insert_bpts(void)
685{
686 int i;
687 struct bpt *bp;
688
689 bp = bpts;
690 for (i = 0; i < NBPTS; ++i, ++bp) {
691 if ((bp->enabled & (BP_TRAP|BP_IABR)) == 0)
692 continue;
693 if (mread(bp->address, &bp->instr[0], 4) != 4) {
694 printf("Couldn't read instruction at %lx, "
695 "disabling breakpoint there\n", bp->address);
696 bp->enabled = 0;
697 continue;
698 }
699 if (IS_MTMSRD(bp->instr[0]) || IS_RFID(bp->instr[0])) {
700 printf("Breakpoint at %lx is on an mtmsrd or rfid "
701 "instruction, disabling it\n", bp->address);
702 bp->enabled = 0;
703 continue;
704 }
705 store_inst(&bp->instr[0]);
706 if (bp->enabled & BP_IABR)
707 continue;
708 if (mwrite(bp->address, &bpinstr, 4) != 4) {
709 printf("Couldn't write instruction at %lx, "
710 "disabling breakpoint there\n", bp->address);
711 bp->enabled &= ~BP_TRAP;
712 continue;
713 }
714 store_inst((void *)bp->address);
715 }
716}
717
718static void insert_cpu_bpts(void)
719{
720 if (dabr.enabled)
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000721 set_dabr(dabr.address | (dabr.enabled & 7));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 if (iabr && cpu_has_feature(CPU_FTR_IABR))
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000723 mtspr(SPRN_IABR, iabr->address
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
725}
726
727static void remove_bpts(void)
728{
729 int i;
730 struct bpt *bp;
731 unsigned instr;
732
733 bp = bpts;
734 for (i = 0; i < NBPTS; ++i, ++bp) {
735 if ((bp->enabled & (BP_TRAP|BP_IABR)) != BP_TRAP)
736 continue;
737 if (mread(bp->address, &instr, 4) == 4
738 && instr == bpinstr
739 && mwrite(bp->address, &bp->instr, 4) != 4)
740 printf("Couldn't remove breakpoint at %lx\n",
741 bp->address);
742 else
743 store_inst((void *)bp->address);
744 }
745}
746
747static void remove_cpu_bpts(void)
748{
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000749 set_dabr(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 if (cpu_has_feature(CPU_FTR_IABR))
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000751 mtspr(SPRN_IABR, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752}
753
754/* Command interpreting routine */
755static char *last_cmd;
756
757static int
758cmds(struct pt_regs *excp)
759{
760 int cmd = 0;
761
762 last_cmd = NULL;
763 xmon_regs = excp;
Olaf Hering26c8af52006-09-08 16:29:21 +0200764
765 if (!xmon_no_auto_backtrace) {
766 xmon_no_auto_backtrace = 1;
767 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
768 }
769
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 for(;;) {
771#ifdef CONFIG_SMP
772 printf("%x:", smp_processor_id());
773#endif /* CONFIG_SMP */
774 printf("mon> ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 flush_input();
776 termch = 0;
777 cmd = skipbl();
778 if( cmd == '\n' ) {
779 if (last_cmd == NULL)
780 continue;
781 take_input(last_cmd);
782 last_cmd = NULL;
783 cmd = inchar();
784 }
785 switch (cmd) {
786 case 'm':
787 cmd = inchar();
788 switch (cmd) {
789 case 'm':
790 case 's':
791 case 'd':
792 memops(cmd);
793 break;
794 case 'l':
795 memlocate();
796 break;
797 case 'z':
798 memzcan();
799 break;
800 case 'i':
801 show_mem();
802 break;
803 default:
804 termch = cmd;
805 memex();
806 }
807 break;
808 case 'd':
809 dump();
810 break;
811 case 'l':
812 symbol_lookup();
813 break;
814 case 'r':
815 prregs(excp); /* print regs */
816 break;
817 case 'e':
818 excprint(excp);
819 break;
820 case 'S':
821 super_regs();
822 break;
823 case 't':
824 backtrace(excp);
825 break;
826 case 'f':
827 cacheflush();
828 break;
829 case 's':
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200830 if (do_spu_cmd() == 0)
831 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 if (do_step(excp))
833 return cmd;
834 break;
835 case 'x':
836 case 'X':
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100837 return cmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 case EOF:
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100839 printf(" <no input ...>\n");
840 mdelay(2000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return cmd;
842 case '?':
Ishizaki Kou4d404ed2007-07-18 19:26:40 +1000843 xmon_puts(help_string);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 case 'b':
846 bpt_cmds();
847 break;
848 case 'C':
849 csum();
850 break;
851 case 'c':
852 if (cpu_cmd())
853 return 0;
854 break;
855 case 'z':
856 bootcmds();
857 break;
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000858 case 'p':
859 proccall();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 break;
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000861#ifdef CONFIG_PPC_STD_MMU
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 case 'u':
863 dump_segments();
864 break;
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000865#endif
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100866#ifdef CONFIG_4xx
867 case 'u':
868 dump_tlb_44x();
869 break;
870#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 default:
872 printf("Unrecognized command: ");
873 do {
874 if (' ' < cmd && cmd <= '~')
875 putchar(cmd);
876 else
877 printf("\\x%x", cmd);
878 cmd = inchar();
879 } while (cmd != '\n');
880 printf(" (type ? for help)\n");
881 break;
882 }
883 }
884}
885
886/*
887 * Step a single instruction.
888 * Some instructions we emulate, others we execute with MSR_SE set.
889 */
890static int do_step(struct pt_regs *regs)
891{
892 unsigned int instr;
893 int stepped;
894
895 /* check we are in 64-bit kernel mode, translation enabled */
896 if ((regs->msr & (MSR_SF|MSR_PR|MSR_IR)) == (MSR_SF|MSR_IR)) {
897 if (mread(regs->nip, &instr, 4) == 4) {
898 stepped = emulate_step(regs, instr);
899 if (stepped < 0) {
900 printf("Couldn't single-step %s instruction\n",
901 (IS_RFID(instr)? "rfid": "mtmsrd"));
902 return 0;
903 }
904 if (stepped > 0) {
905 regs->trap = 0xd00 | (regs->trap & 1);
906 printf("stepped to ");
907 xmon_print_symbol(regs->nip, " ", "\n");
908 ppc_inst_dump(regs->nip, 1, 0);
909 return 0;
910 }
911 }
912 }
913 regs->msr |= MSR_SE;
914 return 1;
915}
916
917static void bootcmds(void)
918{
919 int cmd;
920
921 cmd = inchar();
922 if (cmd == 'r')
923 ppc_md.restart(NULL);
924 else if (cmd == 'h')
925 ppc_md.halt();
926 else if (cmd == 'p')
927 ppc_md.power_off();
928}
929
930static int cpu_cmd(void)
931{
932#ifdef CONFIG_SMP
933 unsigned long cpu;
934 int timeout;
935 int count;
936
937 if (!scanhex(&cpu)) {
938 /* print cpus waiting or in xmon */
939 printf("cpus stopped:");
940 count = 0;
941 for (cpu = 0; cpu < NR_CPUS; ++cpu) {
942 if (cpu_isset(cpu, cpus_in_xmon)) {
943 if (count == 0)
944 printf(" %x", cpu);
945 ++count;
946 } else {
947 if (count > 1)
948 printf("-%x", cpu - 1);
949 count = 0;
950 }
951 }
952 if (count > 1)
953 printf("-%x", NR_CPUS - 1);
954 printf("\n");
955 return 0;
956 }
957 /* try to switch to cpu specified */
958 if (!cpu_isset(cpu, cpus_in_xmon)) {
959 printf("cpu 0x%x isn't in xmon\n", cpu);
960 return 0;
961 }
962 xmon_taken = 0;
963 mb();
964 xmon_owner = cpu;
965 timeout = 10000000;
966 while (!xmon_taken) {
967 if (--timeout == 0) {
968 if (test_and_set_bit(0, &xmon_taken))
969 break;
970 /* take control back */
971 mb();
972 xmon_owner = smp_processor_id();
973 printf("cpu %u didn't take control\n", cpu);
974 return 0;
975 }
976 barrier();
977 }
978 return 1;
979#else
980 return 0;
981#endif /* CONFIG_SMP */
982}
983
984static unsigned short fcstab[256] = {
985 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
986 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
987 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
988 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
989 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
990 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
991 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
992 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
993 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
994 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
995 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
996 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
997 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
998 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
999 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
1000 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
1001 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
1002 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
1003 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
1004 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
1005 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
1006 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
1007 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
1008 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
1009 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
1010 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
1011 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
1012 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
1013 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
1014 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
1015 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
1016 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
1017};
1018
1019#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
1020
1021static void
1022csum(void)
1023{
1024 unsigned int i;
1025 unsigned short fcs;
1026 unsigned char v;
1027
1028 if (!scanhex(&adrs))
1029 return;
1030 if (!scanhex(&ncsum))
1031 return;
1032 fcs = 0xffff;
1033 for (i = 0; i < ncsum; ++i) {
1034 if (mread(adrs+i, &v, 1) == 0) {
1035 printf("csum stopped at %x\n", adrs+i);
1036 break;
1037 }
1038 fcs = FCS(fcs, v);
1039 }
1040 printf("%x\n", fcs);
1041}
1042
1043/*
1044 * Check if this is a suitable place to put a breakpoint.
1045 */
1046static long check_bp_loc(unsigned long addr)
1047{
1048 unsigned int instr;
1049
1050 addr &= ~3;
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001051 if (!is_kernel_addr(addr)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 printf("Breakpoints may only be placed at kernel addresses\n");
1053 return 0;
1054 }
1055 if (!mread(addr, &instr, sizeof(instr))) {
1056 printf("Can't read instruction at address %lx\n", addr);
1057 return 0;
1058 }
1059 if (IS_MTMSRD(instr) || IS_RFID(instr)) {
1060 printf("Breakpoints may not be placed on mtmsrd or rfid "
1061 "instructions\n");
1062 return 0;
1063 }
1064 return 1;
1065}
1066
1067static char *breakpoint_help_string =
1068 "Breakpoint command usage:\n"
1069 "b show breakpoints\n"
1070 "b <addr> [cnt] set breakpoint at given instr addr\n"
1071 "bc clear all breakpoints\n"
1072 "bc <n/addr> clear breakpoint number n or at addr\n"
1073 "bi <addr> [cnt] set hardware instr breakpoint (POWER3/RS64 only)\n"
1074 "bd <addr> [cnt] set hardware data breakpoint\n"
1075 "";
1076
1077static void
1078bpt_cmds(void)
1079{
1080 int cmd;
1081 unsigned long a;
1082 int mode, i;
1083 struct bpt *bp;
1084 const char badaddr[] = "Only kernel addresses are permitted "
1085 "for breakpoints\n";
1086
1087 cmd = inchar();
1088 switch (cmd) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001089#ifndef CONFIG_8xx
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 case 'd': /* bd - hardware data breakpoint */
1091 mode = 7;
1092 cmd = inchar();
1093 if (cmd == 'r')
1094 mode = 5;
1095 else if (cmd == 'w')
1096 mode = 6;
1097 else
1098 termch = cmd;
1099 dabr.address = 0;
1100 dabr.enabled = 0;
1101 if (scanhex(&dabr.address)) {
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001102 if (!is_kernel_addr(dabr.address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 printf(badaddr);
1104 break;
1105 }
1106 dabr.address &= ~7;
1107 dabr.enabled = mode | BP_DABR;
1108 }
1109 break;
1110
1111 case 'i': /* bi - hardware instr breakpoint */
1112 if (!cpu_has_feature(CPU_FTR_IABR)) {
1113 printf("Hardware instruction breakpoint "
1114 "not supported on this cpu\n");
1115 break;
1116 }
1117 if (iabr) {
1118 iabr->enabled &= ~(BP_IABR | BP_IABR_TE);
1119 iabr = NULL;
1120 }
1121 if (!scanhex(&a))
1122 break;
1123 if (!check_bp_loc(a))
1124 break;
1125 bp = new_breakpoint(a);
1126 if (bp != NULL) {
1127 bp->enabled |= BP_IABR | BP_IABR_TE;
1128 iabr = bp;
1129 }
1130 break;
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001131#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132
1133 case 'c':
1134 if (!scanhex(&a)) {
1135 /* clear all breakpoints */
1136 for (i = 0; i < NBPTS; ++i)
1137 bpts[i].enabled = 0;
1138 iabr = NULL;
1139 dabr.enabled = 0;
1140 printf("All breakpoints cleared\n");
1141 break;
1142 }
1143
1144 if (a <= NBPTS && a >= 1) {
1145 /* assume a breakpoint number */
1146 bp = &bpts[a-1]; /* bp nums are 1 based */
1147 } else {
1148 /* assume a breakpoint address */
1149 bp = at_breakpoint(a);
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001150 if (bp == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 printf("No breakpoint at %x\n", a);
1152 break;
1153 }
1154 }
1155
1156 printf("Cleared breakpoint %x (", BP_NUM(bp));
1157 xmon_print_symbol(bp->address, " ", ")\n");
1158 bp->enabled = 0;
1159 break;
1160
1161 default:
1162 termch = cmd;
1163 cmd = skipbl();
1164 if (cmd == '?') {
1165 printf(breakpoint_help_string);
1166 break;
1167 }
1168 termch = cmd;
1169 if (!scanhex(&a)) {
1170 /* print all breakpoints */
1171 printf(" type address\n");
1172 if (dabr.enabled) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001173 printf(" data "REG" [", dabr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 if (dabr.enabled & 1)
1175 printf("r");
1176 if (dabr.enabled & 2)
1177 printf("w");
1178 printf("]\n");
1179 }
1180 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
1181 if (!bp->enabled)
1182 continue;
1183 printf("%2x %s ", BP_NUM(bp),
1184 (bp->enabled & BP_IABR)? "inst": "trap");
1185 xmon_print_symbol(bp->address, " ", "\n");
1186 }
1187 break;
1188 }
1189
1190 if (!check_bp_loc(a))
1191 break;
1192 bp = new_breakpoint(a);
1193 if (bp != NULL)
1194 bp->enabled |= BP_TRAP;
1195 break;
1196 }
1197}
1198
1199/* Very cheap human name for vector lookup. */
1200static
1201const char *getvecname(unsigned long vec)
1202{
1203 char *ret;
1204
1205 switch (vec) {
1206 case 0x100: ret = "(System Reset)"; break;
1207 case 0x200: ret = "(Machine Check)"; break;
1208 case 0x300: ret = "(Data Access)"; break;
1209 case 0x380: ret = "(Data SLB Access)"; break;
1210 case 0x400: ret = "(Instruction Access)"; break;
1211 case 0x480: ret = "(Instruction SLB Access)"; break;
1212 case 0x500: ret = "(Hardware Interrupt)"; break;
1213 case 0x600: ret = "(Alignment)"; break;
1214 case 0x700: ret = "(Program Check)"; break;
1215 case 0x800: ret = "(FPU Unavailable)"; break;
1216 case 0x900: ret = "(Decrementer)"; break;
1217 case 0xc00: ret = "(System Call)"; break;
1218 case 0xd00: ret = "(Single Step)"; break;
1219 case 0xf00: ret = "(Performance Monitor)"; break;
1220 case 0xf20: ret = "(Altivec Unavailable)"; break;
1221 case 0x1300: ret = "(Instruction Breakpoint)"; break;
1222 default: ret = "";
1223 }
1224 return ret;
1225}
1226
1227static void get_function_bounds(unsigned long pc, unsigned long *startp,
1228 unsigned long *endp)
1229{
1230 unsigned long size, offset;
1231 const char *name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232
1233 *startp = *endp = 0;
1234 if (pc == 0)
1235 return;
1236 if (setjmp(bus_error_jmp) == 0) {
1237 catch_memory_errors = 1;
1238 sync();
Alexey Dobriyanffb45122007-05-08 00:28:41 -07001239 name = kallsyms_lookup(pc, &size, &offset, NULL, tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 if (name != NULL) {
1241 *startp = pc - offset;
1242 *endp = pc - offset + size;
1243 }
1244 sync();
1245 }
1246 catch_memory_errors = 0;
1247}
1248
1249static int xmon_depth_to_print = 64;
1250
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001251#define LRSAVE_OFFSET (STACK_FRAME_LR_SAVE * sizeof(unsigned long))
1252#define MARKER_OFFSET (STACK_FRAME_MARKER * sizeof(unsigned long))
1253
1254#ifdef __powerpc64__
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001255#define REGS_OFFSET 0x70
1256#else
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001257#define REGS_OFFSET 16
1258#endif
1259
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260static void xmon_show_stack(unsigned long sp, unsigned long lr,
1261 unsigned long pc)
1262{
1263 unsigned long ip;
1264 unsigned long newsp;
1265 unsigned long marker;
1266 int count = 0;
1267 struct pt_regs regs;
1268
1269 do {
1270 if (sp < PAGE_OFFSET) {
1271 if (sp != 0)
1272 printf("SP (%lx) is in userspace\n", sp);
1273 break;
1274 }
1275
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001276 if (!mread(sp + LRSAVE_OFFSET, &ip, sizeof(unsigned long))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 || !mread(sp, &newsp, sizeof(unsigned long))) {
1278 printf("Couldn't read stack frame at %lx\n", sp);
1279 break;
1280 }
1281
1282 /*
1283 * For the first stack frame, try to work out if
1284 * LR and/or the saved LR value in the bottommost
1285 * stack frame are valid.
1286 */
1287 if ((pc | lr) != 0) {
1288 unsigned long fnstart, fnend;
1289 unsigned long nextip;
1290 int printip = 1;
1291
1292 get_function_bounds(pc, &fnstart, &fnend);
1293 nextip = 0;
1294 if (newsp > sp)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001295 mread(newsp + LRSAVE_OFFSET, &nextip,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 sizeof(unsigned long));
1297 if (lr == ip) {
1298 if (lr < PAGE_OFFSET
1299 || (fnstart <= lr && lr < fnend))
1300 printip = 0;
1301 } else if (lr == nextip) {
1302 printip = 0;
1303 } else if (lr >= PAGE_OFFSET
1304 && !(fnstart <= lr && lr < fnend)) {
1305 printf("[link register ] ");
1306 xmon_print_symbol(lr, " ", "\n");
1307 }
1308 if (printip) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001309 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 xmon_print_symbol(ip, " ", " (unreliable)\n");
1311 }
1312 pc = lr = 0;
1313
1314 } else {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001315 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 xmon_print_symbol(ip, " ", "\n");
1317 }
1318
1319 /* Look for "regshere" marker to see if this is
1320 an exception frame. */
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001321 if (mread(sp + MARKER_OFFSET, &marker, sizeof(unsigned long))
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001322 && marker == STACK_FRAME_REGS_MARKER) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001323 if (mread(sp + REGS_OFFSET, &regs, sizeof(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 != sizeof(regs)) {
1325 printf("Couldn't read registers at %lx\n",
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001326 sp + REGS_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 break;
1328 }
1329 printf("--- Exception: %lx %s at ", regs.trap,
1330 getvecname(TRAP(&regs)));
1331 pc = regs.nip;
1332 lr = regs.link;
1333 xmon_print_symbol(pc, " ", "\n");
1334 }
1335
1336 if (newsp == 0)
1337 break;
1338
1339 sp = newsp;
1340 } while (count++ < xmon_depth_to_print);
1341}
1342
1343static void backtrace(struct pt_regs *excp)
1344{
1345 unsigned long sp;
1346
1347 if (scanhex(&sp))
1348 xmon_show_stack(sp, 0, 0);
1349 else
1350 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
1351 scannl();
1352}
1353
1354static void print_bug_trap(struct pt_regs *regs)
1355{
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001356#ifdef CONFIG_BUG
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001357 const struct bug_entry *bug;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 unsigned long addr;
1359
1360 if (regs->msr & MSR_PR)
1361 return; /* not in kernel */
1362 addr = regs->nip; /* address of trap instruction */
1363 if (addr < PAGE_OFFSET)
1364 return;
1365 bug = find_bug(regs->nip);
1366 if (bug == NULL)
1367 return;
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001368 if (is_warning_bug(bug))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 return;
1370
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001371#ifdef CONFIG_DEBUG_BUGVERBOSE
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001372 printf("kernel BUG at %s:%u!\n",
1373 bug->file, bug->line);
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001374#else
1375 printf("kernel BUG at %p!\n", (void *)bug->bug_addr);
1376#endif
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001377#endif /* CONFIG_BUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378}
1379
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001380static void excprint(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381{
1382 unsigned long trap;
1383
1384#ifdef CONFIG_SMP
1385 printf("cpu 0x%x: ", smp_processor_id());
1386#endif /* CONFIG_SMP */
1387
1388 trap = TRAP(fp);
1389 printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(trap), fp);
1390 printf(" pc: ");
1391 xmon_print_symbol(fp->nip, ": ", "\n");
1392
1393 printf(" lr: ", fp->link);
1394 xmon_print_symbol(fp->link, ": ", "\n");
1395
1396 printf(" sp: %lx\n", fp->gpr[1]);
1397 printf(" msr: %lx\n", fp->msr);
1398
1399 if (trap == 0x300 || trap == 0x380 || trap == 0x600) {
1400 printf(" dar: %lx\n", fp->dar);
1401 if (trap != 0x380)
1402 printf(" dsisr: %lx\n", fp->dsisr);
1403 }
1404
1405 printf(" current = 0x%lx\n", current);
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001406#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 printf(" paca = 0x%lx\n", get_paca());
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001408#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 if (current) {
1410 printf(" pid = %ld, comm = %s\n",
1411 current->pid, current->comm);
1412 }
1413
1414 if (trap == 0x700)
1415 print_bug_trap(fp);
1416}
1417
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001418static void prregs(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419{
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001420 int n, trap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 unsigned long base;
1422 struct pt_regs regs;
1423
1424 if (scanhex(&base)) {
1425 if (setjmp(bus_error_jmp) == 0) {
1426 catch_memory_errors = 1;
1427 sync();
1428 regs = *(struct pt_regs *)base;
1429 sync();
1430 __delay(200);
1431 } else {
1432 catch_memory_errors = 0;
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001433 printf("*** Error reading registers from "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 base);
1435 return;
1436 }
1437 catch_memory_errors = 0;
1438 fp = &regs;
1439 }
1440
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001441#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 if (FULL_REGS(fp)) {
1443 for (n = 0; n < 16; ++n)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001444 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 n, fp->gpr[n], n+16, fp->gpr[n+16]);
1446 } else {
1447 for (n = 0; n < 7; ++n)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001448 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 n, fp->gpr[n], n+7, fp->gpr[n+7]);
1450 }
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001451#else
1452 for (n = 0; n < 32; ++n) {
1453 printf("R%.2d = %.8x%s", n, fp->gpr[n],
1454 (n & 3) == 3? "\n": " ");
1455 if (n == 12 && !FULL_REGS(fp)) {
1456 printf("\n");
1457 break;
1458 }
1459 }
1460#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 printf("pc = ");
1462 xmon_print_symbol(fp->nip, " ", "\n");
1463 printf("lr = ");
1464 xmon_print_symbol(fp->link, " ", "\n");
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001465 printf("msr = "REG" cr = %.8lx\n", fp->msr, fp->ccr);
1466 printf("ctr = "REG" xer = "REG" trap = %4lx\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 fp->ctr, fp->xer, fp->trap);
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001468 trap = TRAP(fp);
1469 if (trap == 0x300 || trap == 0x380 || trap == 0x600)
1470 printf("dar = "REG" dsisr = %.8lx\n", fp->dar, fp->dsisr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471}
1472
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001473static void cacheflush(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474{
1475 int cmd;
1476 unsigned long nflush;
1477
1478 cmd = inchar();
1479 if (cmd != 'i')
1480 termch = cmd;
1481 scanhex((void *)&adrs);
1482 if (termch != '\n')
1483 termch = 0;
1484 nflush = 1;
1485 scanhex(&nflush);
1486 nflush = (nflush + L1_CACHE_BYTES - 1) / L1_CACHE_BYTES;
1487 if (setjmp(bus_error_jmp) == 0) {
1488 catch_memory_errors = 1;
1489 sync();
1490
1491 if (cmd != 'i') {
1492 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1493 cflush((void *) adrs);
1494 } else {
1495 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1496 cinval((void *) adrs);
1497 }
1498 sync();
1499 /* wait a little while to see if we get a machine check */
1500 __delay(200);
1501 }
1502 catch_memory_errors = 0;
1503}
1504
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001505static unsigned long
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506read_spr(int n)
1507{
1508 unsigned int instrs[2];
1509 unsigned long (*code)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 unsigned long ret = -1UL;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001511#ifdef CONFIG_PPC64
1512 unsigned long opd[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 opd[0] = (unsigned long)instrs;
1515 opd[1] = 0;
1516 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001517 code = (unsigned long (*)(void)) opd;
1518#else
1519 code = (unsigned long (*)(void)) instrs;
1520#endif
1521
1522 /* mfspr r3,n; blr */
1523 instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1524 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 store_inst(instrs);
1526 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
1528 if (setjmp(bus_error_jmp) == 0) {
1529 catch_memory_errors = 1;
1530 sync();
1531
1532 ret = code();
1533
1534 sync();
1535 /* wait a little while to see if we get a machine check */
1536 __delay(200);
1537 n = size;
1538 }
1539
1540 return ret;
1541}
1542
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001543static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544write_spr(int n, unsigned long val)
1545{
1546 unsigned int instrs[2];
1547 unsigned long (*code)(unsigned long);
Paul Mackerras548cceb2005-11-11 22:36:34 +11001548#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 unsigned long opd[3];
1550
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 opd[0] = (unsigned long)instrs;
1552 opd[1] = 0;
1553 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001554 code = (unsigned long (*)(unsigned long)) opd;
1555#else
1556 code = (unsigned long (*)(unsigned long)) instrs;
1557#endif
1558
1559 instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1560 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 store_inst(instrs);
1562 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563
1564 if (setjmp(bus_error_jmp) == 0) {
1565 catch_memory_errors = 1;
1566 sync();
1567
1568 code(val);
1569
1570 sync();
1571 /* wait a little while to see if we get a machine check */
1572 __delay(200);
1573 n = size;
1574 }
1575}
1576
1577static unsigned long regno;
1578extern char exc_prolog;
1579extern char dec_exc;
1580
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001581static void super_regs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582{
1583 int cmd;
1584 unsigned long val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585
1586 cmd = skipbl();
1587 if (cmd == '\n') {
1588 unsigned long sp, toc;
1589 asm("mr %0,1" : "=r" (sp) :);
1590 asm("mr %0,2" : "=r" (toc) :);
1591
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001592 printf("msr = "REG" sprg0= "REG"\n",
1593 mfmsr(), mfspr(SPRN_SPRG0));
1594 printf("pvr = "REG" sprg1= "REG"\n",
1595 mfspr(SPRN_PVR), mfspr(SPRN_SPRG1));
1596 printf("dec = "REG" sprg2= "REG"\n",
1597 mfspr(SPRN_DEC), mfspr(SPRN_SPRG2));
1598 printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3));
1599 printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600#ifdef CONFIG_PPC_ISERIES
Stephen Rothwell1d135812006-11-13 14:50:28 +11001601 if (firmware_has_feature(FW_FEATURE_ISERIES)) {
1602 struct paca_struct *ptrPaca;
1603 struct lppaca *ptrLpPaca;
Stephen Rothwell1d135812006-11-13 14:50:28 +11001604
1605 /* Dump out relevant Paca data areas. */
1606 printf("Paca: \n");
1607 ptrPaca = get_paca();
1608
1609 printf(" Local Processor Control Area (LpPaca): \n");
1610 ptrLpPaca = ptrPaca->lppaca_ptr;
1611 printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n",
1612 ptrLpPaca->saved_srr0, ptrLpPaca->saved_srr1);
1613 printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n",
1614 ptrLpPaca->saved_gpr3, ptrLpPaca->saved_gpr4);
1615 printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->saved_gpr5);
Stephen Rothwell1d135812006-11-13 14:50:28 +11001616 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617#endif
1618
1619 return;
1620 }
1621
1622 scanhex(&regno);
1623 switch (cmd) {
1624 case 'w':
1625 val = read_spr(regno);
1626 scanhex(&val);
1627 write_spr(regno, val);
1628 /* fall through */
1629 case 'r':
1630 printf("spr %lx = %lx\n", regno, read_spr(regno));
1631 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 }
1633 scannl();
1634}
1635
1636/*
1637 * Stuff for reading and writing memory safely
1638 */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001639static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640mread(unsigned long adrs, void *buf, int size)
1641{
1642 volatile int n;
1643 char *p, *q;
1644
1645 n = 0;
1646 if (setjmp(bus_error_jmp) == 0) {
1647 catch_memory_errors = 1;
1648 sync();
1649 p = (char *)adrs;
1650 q = (char *)buf;
1651 switch (size) {
1652 case 2:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001653 *(u16 *)q = *(u16 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 break;
1655 case 4:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001656 *(u32 *)q = *(u32 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 break;
1658 case 8:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001659 *(u64 *)q = *(u64 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 break;
1661 default:
1662 for( ; n < size; ++n) {
1663 *q++ = *p++;
1664 sync();
1665 }
1666 }
1667 sync();
1668 /* wait a little while to see if we get a machine check */
1669 __delay(200);
1670 n = size;
1671 }
1672 catch_memory_errors = 0;
1673 return n;
1674}
1675
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001676static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677mwrite(unsigned long adrs, void *buf, int size)
1678{
1679 volatile int n;
1680 char *p, *q;
1681
1682 n = 0;
1683 if (setjmp(bus_error_jmp) == 0) {
1684 catch_memory_errors = 1;
1685 sync();
1686 p = (char *) adrs;
1687 q = (char *) buf;
1688 switch (size) {
1689 case 2:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001690 *(u16 *)p = *(u16 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 break;
1692 case 4:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001693 *(u32 *)p = *(u32 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 break;
1695 case 8:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001696 *(u64 *)p = *(u64 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 break;
1698 default:
1699 for ( ; n < size; ++n) {
1700 *p++ = *q++;
1701 sync();
1702 }
1703 }
1704 sync();
1705 /* wait a little while to see if we get a machine check */
1706 __delay(200);
1707 n = size;
1708 } else {
1709 printf("*** Error writing address %x\n", adrs + n);
1710 }
1711 catch_memory_errors = 0;
1712 return n;
1713}
1714
1715static int fault_type;
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001716static int fault_except;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717static char *fault_chars[] = { "--", "**", "##" };
1718
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001719static int handle_fault(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720{
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001721 fault_except = TRAP(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 switch (TRAP(regs)) {
1723 case 0x200:
1724 fault_type = 0;
1725 break;
1726 case 0x300:
1727 case 0x380:
1728 fault_type = 1;
1729 break;
1730 default:
1731 fault_type = 2;
1732 }
1733
1734 longjmp(bus_error_jmp, 1);
1735
1736 return 0;
1737}
1738
1739#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
1740
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001741static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742byterev(unsigned char *val, int size)
1743{
1744 int t;
1745
1746 switch (size) {
1747 case 2:
1748 SWAP(val[0], val[1], t);
1749 break;
1750 case 4:
1751 SWAP(val[0], val[3], t);
1752 SWAP(val[1], val[2], t);
1753 break;
1754 case 8: /* is there really any use for this? */
1755 SWAP(val[0], val[7], t);
1756 SWAP(val[1], val[6], t);
1757 SWAP(val[2], val[5], t);
1758 SWAP(val[3], val[4], t);
1759 break;
1760 }
1761}
1762
1763static int brev;
1764static int mnoread;
1765
1766static char *memex_help_string =
1767 "Memory examine command usage:\n"
1768 "m [addr] [flags] examine/change memory\n"
1769 " addr is optional. will start where left off.\n"
1770 " flags may include chars from this set:\n"
1771 " b modify by bytes (default)\n"
1772 " w modify by words (2 byte)\n"
1773 " l modify by longs (4 byte)\n"
1774 " d modify by doubleword (8 byte)\n"
1775 " r toggle reverse byte order mode\n"
1776 " n do not read memory (for i/o spaces)\n"
1777 " . ok to read (default)\n"
1778 "NOTE: flags are saved as defaults\n"
1779 "";
1780
1781static char *memex_subcmd_help_string =
1782 "Memory examine subcommands:\n"
1783 " hexval write this val to current location\n"
1784 " 'string' write chars from string to this location\n"
1785 " ' increment address\n"
1786 " ^ decrement address\n"
1787 " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n"
1788 " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n"
1789 " ` clear no-read flag\n"
1790 " ; stay at this addr\n"
1791 " v change to byte mode\n"
1792 " w change to word (2 byte) mode\n"
1793 " l change to long (4 byte) mode\n"
1794 " u change to doubleword (8 byte) mode\n"
1795 " m addr change current addr\n"
1796 " n toggle no-read flag\n"
1797 " r toggle byte reverse flag\n"
1798 " < count back up count bytes\n"
1799 " > count skip forward count bytes\n"
1800 " x exit this mode\n"
1801 "";
1802
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001803static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804memex(void)
1805{
1806 int cmd, inc, i, nslash;
1807 unsigned long n;
1808 unsigned char val[16];
1809
1810 scanhex((void *)&adrs);
1811 cmd = skipbl();
1812 if (cmd == '?') {
1813 printf(memex_help_string);
1814 return;
1815 } else {
1816 termch = cmd;
1817 }
1818 last_cmd = "m\n";
1819 while ((cmd = skipbl()) != '\n') {
1820 switch( cmd ){
1821 case 'b': size = 1; break;
1822 case 'w': size = 2; break;
1823 case 'l': size = 4; break;
1824 case 'd': size = 8; break;
1825 case 'r': brev = !brev; break;
1826 case 'n': mnoread = 1; break;
1827 case '.': mnoread = 0; break;
1828 }
1829 }
1830 if( size <= 0 )
1831 size = 1;
1832 else if( size > 8 )
1833 size = 8;
1834 for(;;){
1835 if (!mnoread)
1836 n = mread(adrs, val, size);
Paul Mackerrase1449ed2005-11-10 14:30:20 +11001837 printf(REG"%c", adrs, brev? 'r': ' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838 if (!mnoread) {
1839 if (brev)
1840 byterev(val, size);
1841 putchar(' ');
1842 for (i = 0; i < n; ++i)
1843 printf("%.2x", val[i]);
1844 for (; i < size; ++i)
1845 printf("%s", fault_chars[fault_type]);
1846 }
1847 putchar(' ');
1848 inc = size;
1849 nslash = 0;
1850 for(;;){
1851 if( scanhex(&n) ){
1852 for (i = 0; i < size; ++i)
1853 val[i] = n >> (i * 8);
1854 if (!brev)
1855 byterev(val, size);
1856 mwrite(adrs, val, size);
1857 inc = size;
1858 }
1859 cmd = skipbl();
1860 if (cmd == '\n')
1861 break;
1862 inc = 0;
1863 switch (cmd) {
1864 case '\'':
1865 for(;;){
1866 n = inchar();
1867 if( n == '\\' )
1868 n = bsesc();
1869 else if( n == '\'' )
1870 break;
1871 for (i = 0; i < size; ++i)
1872 val[i] = n >> (i * 8);
1873 if (!brev)
1874 byterev(val, size);
1875 mwrite(adrs, val, size);
1876 adrs += size;
1877 }
1878 adrs -= size;
1879 inc = size;
1880 break;
1881 case ',':
1882 adrs += size;
1883 break;
1884 case '.':
1885 mnoread = 0;
1886 break;
1887 case ';':
1888 break;
1889 case 'x':
1890 case EOF:
1891 scannl();
1892 return;
1893 case 'b':
1894 case 'v':
1895 size = 1;
1896 break;
1897 case 'w':
1898 size = 2;
1899 break;
1900 case 'l':
1901 size = 4;
1902 break;
1903 case 'u':
1904 size = 8;
1905 break;
1906 case '^':
1907 adrs -= size;
1908 break;
1909 break;
1910 case '/':
1911 if (nslash > 0)
1912 adrs -= 1 << nslash;
1913 else
1914 nslash = 0;
1915 nslash += 4;
1916 adrs += 1 << nslash;
1917 break;
1918 case '\\':
1919 if (nslash < 0)
1920 adrs += 1 << -nslash;
1921 else
1922 nslash = 0;
1923 nslash -= 4;
1924 adrs -= 1 << -nslash;
1925 break;
1926 case 'm':
1927 scanhex((void *)&adrs);
1928 break;
1929 case 'n':
1930 mnoread = 1;
1931 break;
1932 case 'r':
1933 brev = !brev;
1934 break;
1935 case '<':
1936 n = size;
1937 scanhex(&n);
1938 adrs -= n;
1939 break;
1940 case '>':
1941 n = size;
1942 scanhex(&n);
1943 adrs += n;
1944 break;
1945 case '?':
1946 printf(memex_subcmd_help_string);
1947 break;
1948 }
1949 }
1950 adrs += inc;
1951 }
1952}
1953
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001954static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955bsesc(void)
1956{
1957 int c;
1958
1959 c = inchar();
1960 switch( c ){
1961 case 'n': c = '\n'; break;
1962 case 'r': c = '\r'; break;
1963 case 'b': c = '\b'; break;
1964 case 't': c = '\t'; break;
1965 }
1966 return c;
1967}
1968
Olaf Hering7e5b5932006-03-08 20:40:28 +01001969static void xmon_rawdump (unsigned long adrs, long ndump)
1970{
1971 long n, m, r, nr;
1972 unsigned char temp[16];
1973
1974 for (n = ndump; n > 0;) {
1975 r = n < 16? n: 16;
1976 nr = mread(adrs, temp, r);
1977 adrs += nr;
1978 for (m = 0; m < r; ++m) {
1979 if (m < nr)
1980 printf("%.2x", temp[m]);
1981 else
1982 printf("%s", fault_chars[fault_type]);
1983 }
1984 n -= r;
1985 if (nr < r)
1986 break;
1987 }
1988 printf("\n");
1989}
1990
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991#define isxdigit(c) (('0' <= (c) && (c) <= '9') \
1992 || ('a' <= (c) && (c) <= 'f') \
1993 || ('A' <= (c) && (c) <= 'F'))
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001994static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995dump(void)
1996{
1997 int c;
1998
1999 c = inchar();
2000 if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n')
2001 termch = c;
2002 scanhex((void *)&adrs);
2003 if (termch != '\n')
2004 termch = 0;
2005 if (c == 'i') {
2006 scanhex(&nidump);
2007 if (nidump == 0)
2008 nidump = 16;
2009 else if (nidump > MAX_DUMP)
2010 nidump = MAX_DUMP;
2011 adrs += ppc_inst_dump(adrs, nidump, 1);
2012 last_cmd = "di\n";
Olaf Hering7e5b5932006-03-08 20:40:28 +01002013 } else if (c == 'r') {
2014 scanhex(&ndump);
2015 if (ndump == 0)
2016 ndump = 64;
2017 xmon_rawdump(adrs, ndump);
2018 adrs += ndump;
2019 last_cmd = "dr\n";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 } else {
2021 scanhex(&ndump);
2022 if (ndump == 0)
2023 ndump = 64;
2024 else if (ndump > MAX_DUMP)
2025 ndump = MAX_DUMP;
2026 prdump(adrs, ndump);
2027 adrs += ndump;
2028 last_cmd = "d\n";
2029 }
2030}
2031
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002032static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033prdump(unsigned long adrs, long ndump)
2034{
2035 long n, m, c, r, nr;
2036 unsigned char temp[16];
2037
2038 for (n = ndump; n > 0;) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002039 printf(REG, adrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 putchar(' ');
2041 r = n < 16? n: 16;
2042 nr = mread(adrs, temp, r);
2043 adrs += nr;
2044 for (m = 0; m < r; ++m) {
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002045 if ((m & (sizeof(long) - 1)) == 0 && m > 0)
2046 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 if (m < nr)
2048 printf("%.2x", temp[m]);
2049 else
2050 printf("%s", fault_chars[fault_type]);
2051 }
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002052 for (; m < 16; ++m) {
2053 if ((m & (sizeof(long) - 1)) == 0)
2054 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 printf(" ");
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002056 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057 printf(" |");
2058 for (m = 0; m < r; ++m) {
2059 if (m < nr) {
2060 c = temp[m];
2061 putchar(' ' <= c && c <= '~'? c: '.');
2062 } else
2063 putchar(' ');
2064 }
2065 n -= r;
2066 for (; m < 16; ++m)
2067 putchar(' ');
2068 printf("|\n");
2069 if (nr < r)
2070 break;
2071 }
2072}
2073
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002074typedef int (*instruction_dump_func)(unsigned long inst, unsigned long addr);
2075
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002076static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002077generic_inst_dump(unsigned long adr, long count, int praddr,
2078 instruction_dump_func dump_func)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079{
2080 int nr, dotted;
2081 unsigned long first_adr;
2082 unsigned long inst, last_inst = 0;
2083 unsigned char val[4];
2084
2085 dotted = 0;
2086 for (first_adr = adr; count > 0; --count, adr += 4) {
2087 nr = mread(adr, val, 4);
2088 if (nr == 0) {
2089 if (praddr) {
2090 const char *x = fault_chars[fault_type];
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002091 printf(REG" %s%s%s%s\n", adr, x, x, x, x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 }
2093 break;
2094 }
2095 inst = GETWORD(val);
2096 if (adr > first_adr && inst == last_inst) {
2097 if (!dotted) {
2098 printf(" ...\n");
2099 dotted = 1;
2100 }
2101 continue;
2102 }
2103 dotted = 0;
2104 last_inst = inst;
2105 if (praddr)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002106 printf(REG" %.8x", adr, inst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 printf("\t");
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002108 dump_func(inst, adr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 printf("\n");
2110 }
2111 return adr - first_adr;
2112}
2113
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002114static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002115ppc_inst_dump(unsigned long adr, long count, int praddr)
2116{
2117 return generic_inst_dump(adr, count, praddr, print_insn_powerpc);
2118}
2119
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120void
2121print_address(unsigned long addr)
2122{
2123 xmon_print_symbol(addr, "\t# ", "");
2124}
2125
2126
2127/*
2128 * Memory operations - move, set, print differences
2129 */
2130static unsigned long mdest; /* destination address */
2131static unsigned long msrc; /* source address */
2132static unsigned long mval; /* byte value to set memory to */
2133static unsigned long mcount; /* # bytes to affect */
2134static unsigned long mdiffs; /* max # differences to print */
2135
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002136static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137memops(int cmd)
2138{
2139 scanhex((void *)&mdest);
2140 if( termch != '\n' )
2141 termch = 0;
2142 scanhex((void *)(cmd == 's'? &mval: &msrc));
2143 if( termch != '\n' )
2144 termch = 0;
2145 scanhex((void *)&mcount);
2146 switch( cmd ){
2147 case 'm':
2148 memmove((void *)mdest, (void *)msrc, mcount);
2149 break;
2150 case 's':
2151 memset((void *)mdest, mval, mcount);
2152 break;
2153 case 'd':
2154 if( termch != '\n' )
2155 termch = 0;
2156 scanhex((void *)&mdiffs);
2157 memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs);
2158 break;
2159 }
2160}
2161
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002162static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr)
2164{
2165 unsigned n, prt;
2166
2167 prt = 0;
2168 for( n = nb; n > 0; --n )
2169 if( *p1++ != *p2++ )
2170 if( ++prt <= maxpr )
2171 printf("%.16x %.2x # %.16x %.2x\n", p1 - 1,
2172 p1[-1], p2 - 1, p2[-1]);
2173 if( prt > maxpr )
2174 printf("Total of %d differences\n", prt);
2175}
2176
2177static unsigned mend;
2178static unsigned mask;
2179
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002180static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181memlocate(void)
2182{
2183 unsigned a, n;
2184 unsigned char val[4];
2185
2186 last_cmd = "ml";
2187 scanhex((void *)&mdest);
2188 if (termch != '\n') {
2189 termch = 0;
2190 scanhex((void *)&mend);
2191 if (termch != '\n') {
2192 termch = 0;
2193 scanhex((void *)&mval);
2194 mask = ~0;
2195 if (termch != '\n') termch = 0;
2196 scanhex((void *)&mask);
2197 }
2198 }
2199 n = 0;
2200 for (a = mdest; a < mend; a += 4) {
2201 if (mread(a, val, 4) == 4
2202 && ((GETWORD(val) ^ mval) & mask) == 0) {
2203 printf("%.16x: %.16x\n", a, GETWORD(val));
2204 if (++n >= 10)
2205 break;
2206 }
2207 }
2208}
2209
2210static unsigned long mskip = 0x1000;
2211static unsigned long mlim = 0xffffffff;
2212
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002213static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214memzcan(void)
2215{
2216 unsigned char v;
2217 unsigned a;
2218 int ok, ook;
2219
2220 scanhex(&mdest);
2221 if (termch != '\n') termch = 0;
2222 scanhex(&mskip);
2223 if (termch != '\n') termch = 0;
2224 scanhex(&mlim);
2225 ook = 0;
2226 for (a = mdest; a < mlim; a += mskip) {
2227 ok = mread(a, &v, 1);
2228 if (ok && !ook) {
2229 printf("%.8x .. ", a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 } else if (!ok && ook)
2231 printf("%.8x\n", a - mskip);
2232 ook = ok;
2233 if (a + mskip < a)
2234 break;
2235 }
2236 if (ook)
2237 printf("%.8x\n", a - mskip);
2238}
2239
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002240static void proccall(void)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002241{
2242 unsigned long args[8];
2243 unsigned long ret;
2244 int i;
2245 typedef unsigned long (*callfunc_t)(unsigned long, unsigned long,
2246 unsigned long, unsigned long, unsigned long,
2247 unsigned long, unsigned long, unsigned long);
2248 callfunc_t func;
2249
2250 if (!scanhex(&adrs))
2251 return;
2252 if (termch != '\n')
2253 termch = 0;
2254 for (i = 0; i < 8; ++i)
2255 args[i] = 0;
2256 for (i = 0; i < 8; ++i) {
2257 if (!scanhex(&args[i]) || termch == '\n')
2258 break;
2259 termch = 0;
2260 }
2261 func = (callfunc_t) adrs;
2262 ret = 0;
2263 if (setjmp(bus_error_jmp) == 0) {
2264 catch_memory_errors = 1;
2265 sync();
2266 ret = func(args[0], args[1], args[2], args[3],
2267 args[4], args[5], args[6], args[7]);
2268 sync();
2269 printf("return value is %x\n", ret);
2270 } else {
2271 printf("*** %x exception occurred\n", fault_except);
2272 }
2273 catch_memory_errors = 0;
2274}
2275
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276/* Input scanning routines */
2277int
2278skipbl(void)
2279{
2280 int c;
2281
2282 if( termch != 0 ){
2283 c = termch;
2284 termch = 0;
2285 } else
2286 c = inchar();
2287 while( c == ' ' || c == '\t' )
2288 c = inchar();
2289 return c;
2290}
2291
2292#define N_PTREGS 44
2293static char *regnames[N_PTREGS] = {
2294 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
2295 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
2296 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
2297 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002298 "pc", "msr", "or3", "ctr", "lr", "xer", "ccr",
2299#ifdef CONFIG_PPC64
2300 "softe",
2301#else
2302 "mq",
2303#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 "trap", "dar", "dsisr", "res"
2305};
2306
2307int
2308scanhex(unsigned long *vp)
2309{
2310 int c, d;
2311 unsigned long v;
2312
2313 c = skipbl();
2314 if (c == '%') {
2315 /* parse register name */
2316 char regname[8];
2317 int i;
2318
2319 for (i = 0; i < sizeof(regname) - 1; ++i) {
2320 c = inchar();
2321 if (!isalnum(c)) {
2322 termch = c;
2323 break;
2324 }
2325 regname[i] = c;
2326 }
2327 regname[i] = 0;
2328 for (i = 0; i < N_PTREGS; ++i) {
2329 if (strcmp(regnames[i], regname) == 0) {
2330 if (xmon_regs == NULL) {
2331 printf("regs not available\n");
2332 return 0;
2333 }
2334 *vp = ((unsigned long *)xmon_regs)[i];
2335 return 1;
2336 }
2337 }
2338 printf("invalid register name '%%%s'\n", regname);
2339 return 0;
2340 }
2341
2342 /* skip leading "0x" if any */
2343
2344 if (c == '0') {
2345 c = inchar();
2346 if (c == 'x') {
2347 c = inchar();
2348 } else {
2349 d = hexdigit(c);
2350 if (d == EOF) {
2351 termch = c;
2352 *vp = 0;
2353 return 1;
2354 }
2355 }
2356 } else if (c == '$') {
2357 int i;
2358 for (i=0; i<63; i++) {
2359 c = inchar();
2360 if (isspace(c)) {
2361 termch = c;
2362 break;
2363 }
2364 tmpstr[i] = c;
2365 }
2366 tmpstr[i++] = 0;
Benjamin Herrenschmidt6879dc12005-06-21 17:15:30 -07002367 *vp = 0;
2368 if (setjmp(bus_error_jmp) == 0) {
2369 catch_memory_errors = 1;
2370 sync();
2371 *vp = kallsyms_lookup_name(tmpstr);
2372 sync();
2373 }
2374 catch_memory_errors = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375 if (!(*vp)) {
2376 printf("unknown symbol '%s'\n", tmpstr);
2377 return 0;
2378 }
2379 return 1;
2380 }
2381
2382 d = hexdigit(c);
2383 if (d == EOF) {
2384 termch = c;
2385 return 0;
2386 }
2387 v = 0;
2388 do {
2389 v = (v << 4) + d;
2390 c = inchar();
2391 d = hexdigit(c);
2392 } while (d != EOF);
2393 termch = c;
2394 *vp = v;
2395 return 1;
2396}
2397
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002398static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399scannl(void)
2400{
2401 int c;
2402
2403 c = termch;
2404 termch = 0;
2405 while( c != '\n' )
2406 c = inchar();
2407}
2408
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002409static int hexdigit(int c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410{
2411 if( '0' <= c && c <= '9' )
2412 return c - '0';
2413 if( 'A' <= c && c <= 'F' )
2414 return c - ('A' - 10);
2415 if( 'a' <= c && c <= 'f' )
2416 return c - ('a' - 10);
2417 return EOF;
2418}
2419
2420void
2421getstring(char *s, int size)
2422{
2423 int c;
2424
2425 c = skipbl();
2426 do {
2427 if( size > 1 ){
2428 *s++ = c;
2429 --size;
2430 }
2431 c = inchar();
2432 } while( c != ' ' && c != '\t' && c != '\n' );
2433 termch = c;
2434 *s = 0;
2435}
2436
2437static char line[256];
2438static char *lineptr;
2439
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002440static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441flush_input(void)
2442{
2443 lineptr = NULL;
2444}
2445
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002446static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447inchar(void)
2448{
2449 if (lineptr == NULL || *lineptr == 0) {
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002450 if (xmon_gets(line, sizeof(line)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451 lineptr = NULL;
2452 return EOF;
2453 }
2454 lineptr = line;
2455 }
2456 return *lineptr++;
2457}
2458
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002459static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460take_input(char *str)
2461{
2462 lineptr = str;
2463}
2464
2465
2466static void
2467symbol_lookup(void)
2468{
2469 int type = inchar();
2470 unsigned long addr;
2471 static char tmp[64];
2472
2473 switch (type) {
2474 case 'a':
2475 if (scanhex(&addr))
2476 xmon_print_symbol(addr, ": ", "\n");
2477 termch = 0;
2478 break;
2479 case 's':
2480 getstring(tmp, 64);
2481 if (setjmp(bus_error_jmp) == 0) {
2482 catch_memory_errors = 1;
2483 sync();
2484 addr = kallsyms_lookup_name(tmp);
2485 if (addr)
2486 printf("%s: %lx\n", tmp, addr);
2487 else
2488 printf("Symbol '%s' not found.\n", tmp);
2489 sync();
2490 }
2491 catch_memory_errors = 0;
2492 termch = 0;
2493 break;
2494 }
2495}
2496
2497
2498/* Print an address in numeric and symbolic form (if possible) */
2499static void xmon_print_symbol(unsigned long address, const char *mid,
2500 const char *after)
2501{
2502 char *modname;
2503 const char *name = NULL;
2504 unsigned long offset, size;
2505
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002506 printf(REG, address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 if (setjmp(bus_error_jmp) == 0) {
2508 catch_memory_errors = 1;
2509 sync();
2510 name = kallsyms_lookup(address, &size, &offset, &modname,
2511 tmpstr);
2512 sync();
2513 /* wait a little while to see if we get a machine check */
2514 __delay(200);
2515 }
2516
2517 catch_memory_errors = 0;
2518
2519 if (name) {
2520 printf("%s%s+%#lx/%#lx", mid, name, offset, size);
2521 if (modname)
2522 printf(" [%s]", modname);
2523 }
2524 printf("%s", after);
2525}
2526
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002527#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528static void dump_slb(void)
2529{
2530 int i;
will schmidtb3b95952007-12-07 08:22:23 +11002531 unsigned long esid,vsid,valid;
2532 unsigned long llp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533
2534 printf("SLB contents of cpu %x\n", smp_processor_id());
2535
Michael Neuling584f8b72007-12-06 17:24:48 +11002536 for (i = 0; i < mmu_slb_size; i++) {
will schmidtb3b95952007-12-07 08:22:23 +11002537 asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i));
2538 asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i));
2539 valid = (esid & SLB_ESID_V);
2540 if (valid | esid | vsid) {
2541 printf("%02d %016lx %016lx", i, esid, vsid);
2542 if (valid) {
2543 llp = vsid & SLB_VSID_LLP;
2544 if (vsid & SLB_VSID_B_1T) {
2545 printf(" 1T ESID=%9lx VSID=%13lx LLP:%3lx \n",
2546 GET_ESID_1T(esid),
2547 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT_1T,
2548 llp);
2549 } else {
2550 printf(" 256M ESID=%9lx VSID=%13lx LLP:%3lx \n",
2551 GET_ESID(esid),
2552 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT,
2553 llp);
2554 }
2555 } else
2556 printf("\n");
2557 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 }
2559}
2560
2561static void dump_stab(void)
2562{
2563 int i;
2564 unsigned long *tmp = (unsigned long *)get_paca()->stab_addr;
2565
2566 printf("Segment table contents of cpu %x\n", smp_processor_id());
2567
2568 for (i = 0; i < PAGE_SIZE/16; i++) {
2569 unsigned long a, b;
2570
2571 a = *tmp++;
2572 b = *tmp++;
2573
2574 if (a || b) {
2575 printf("%03d %016lx ", i, a);
2576 printf("%016lx\n", b);
2577 }
2578 }
2579}
2580
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002581void dump_segments(void)
2582{
2583 if (cpu_has_feature(CPU_FTR_SLB))
2584 dump_slb();
2585 else
2586 dump_stab();
2587}
2588#endif
2589
2590#ifdef CONFIG_PPC_STD_MMU_32
2591void dump_segments(void)
2592{
2593 int i;
2594
2595 printf("sr0-15 =");
2596 for (i = 0; i < 16; ++i)
2597 printf(" %x", mfsrin(i));
2598 printf("\n");
2599}
2600#endif
2601
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +11002602#ifdef CONFIG_44x
2603static void dump_tlb_44x(void)
2604{
2605 int i;
2606
2607 for (i = 0; i < PPC44x_TLB_SIZE; i++) {
2608 unsigned long w0,w1,w2;
2609 asm volatile("tlbre %0,%1,0" : "=r" (w0) : "r" (i));
2610 asm volatile("tlbre %0,%1,1" : "=r" (w1) : "r" (i));
2611 asm volatile("tlbre %0,%1,2" : "=r" (w2) : "r" (i));
2612 printf("[%02x] %08x %08x %08x ", i, w0, w1, w2);
2613 if (w0 & PPC44x_TLB_VALID) {
2614 printf("V %08x -> %01x%08x %c%c%c%c%c",
2615 w0 & PPC44x_TLB_EPN_MASK,
2616 w1 & PPC44x_TLB_ERPN_MASK,
2617 w1 & PPC44x_TLB_RPN_MASK,
2618 (w2 & PPC44x_TLB_W) ? 'W' : 'w',
2619 (w2 & PPC44x_TLB_I) ? 'I' : 'i',
2620 (w2 & PPC44x_TLB_M) ? 'M' : 'm',
2621 (w2 & PPC44x_TLB_G) ? 'G' : 'g',
2622 (w2 & PPC44x_TLB_E) ? 'E' : 'e');
2623 }
2624 printf("\n");
2625 }
2626}
2627#endif /* CONFIG_44x */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002628
2629static void xmon_init(int enable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002630{
Stephen Rothwellbbb68172006-11-30 11:44:09 +11002631#ifdef CONFIG_PPC_ISERIES
2632 if (firmware_has_feature(FW_FEATURE_ISERIES))
2633 return;
2634#endif
Olaf Heringb13cfd172005-08-04 19:26:42 +02002635 if (enable) {
2636 __debugger = xmon;
2637 __debugger_ipi = xmon_ipi;
2638 __debugger_bpt = xmon_bpt;
2639 __debugger_sstep = xmon_sstep;
2640 __debugger_iabr_match = xmon_iabr_match;
2641 __debugger_dabr_match = xmon_dabr_match;
2642 __debugger_fault_handler = xmon_fault_handler;
2643 } else {
2644 __debugger = NULL;
2645 __debugger_ipi = NULL;
2646 __debugger_bpt = NULL;
2647 __debugger_sstep = NULL;
2648 __debugger_iabr_match = NULL;
2649 __debugger_dabr_match = NULL;
2650 __debugger_fault_handler = NULL;
2651 }
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002652 xmon_map_scc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653}
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002654
2655#ifdef CONFIG_MAGIC_SYSRQ
David Howells7d12e782006-10-05 14:55:46 +01002656static void sysrq_handle_xmon(int key, struct tty_struct *tty)
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002657{
2658 /* ensure xmon is enabled */
2659 xmon_init(1);
David Howells7d12e782006-10-05 14:55:46 +01002660 debugger(get_irq_regs());
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002661}
2662
2663static struct sysrq_key_op sysrq_xmon_op =
2664{
2665 .handler = sysrq_handle_xmon,
2666 .help_msg = "Xmon",
2667 .action_msg = "Entering xmon",
2668};
2669
2670static int __init setup_xmon_sysrq(void)
2671{
Stephen Rothwellbbb68172006-11-30 11:44:09 +11002672#ifdef CONFIG_PPC_ISERIES
2673 if (firmware_has_feature(FW_FEATURE_ISERIES))
2674 return 0;
2675#endif
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002676 register_sysrq_key('x', &sysrq_xmon_op);
2677 return 0;
2678}
2679__initcall(setup_xmon_sysrq);
2680#endif /* CONFIG_MAGIC_SYSRQ */
Michael Ellerman476792832006-10-03 14:12:08 +10002681
Olaf Heringf5e6a282007-06-24 16:57:08 +10002682static int __initdata xmon_early, xmon_off;
Michael Ellerman476792832006-10-03 14:12:08 +10002683
2684static int __init early_parse_xmon(char *p)
2685{
2686 if (!p || strncmp(p, "early", 5) == 0) {
2687 /* just "xmon" is equivalent to "xmon=early" */
2688 xmon_init(1);
2689 xmon_early = 1;
2690 } else if (strncmp(p, "on", 2) == 0)
2691 xmon_init(1);
2692 else if (strncmp(p, "off", 3) == 0)
2693 xmon_off = 1;
2694 else if (strncmp(p, "nobt", 4) == 0)
2695 xmon_no_auto_backtrace = 1;
2696 else
2697 return 1;
2698
2699 return 0;
2700}
2701early_param("xmon", early_parse_xmon);
2702
2703void __init xmon_setup(void)
2704{
2705#ifdef CONFIG_XMON_DEFAULT
2706 if (!xmon_off)
2707 xmon_init(1);
2708#endif
2709 if (xmon_early)
2710 debugger(NULL);
2711}
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002712
Arnd Bergmanne0555952006-11-27 19:18:55 +01002713#ifdef CONFIG_SPU_BASE
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002714
2715struct spu_info {
2716 struct spu *spu;
2717 u64 saved_mfc_sr1_RW;
2718 u32 saved_spu_runcntl_RW;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002719 unsigned long dump_addr;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002720 u8 stopped_ok;
2721};
2722
2723#define XMON_NUM_SPUS 16 /* Enough for current hardware */
2724
2725static struct spu_info spu_info[XMON_NUM_SPUS];
2726
2727void xmon_register_spus(struct list_head *list)
2728{
2729 struct spu *spu;
2730
2731 list_for_each_entry(spu, list, full_list) {
2732 if (spu->number >= XMON_NUM_SPUS) {
2733 WARN_ON(1);
2734 continue;
2735 }
2736
2737 spu_info[spu->number].spu = spu;
2738 spu_info[spu->number].stopped_ok = 0;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002739 spu_info[spu->number].dump_addr = (unsigned long)
2740 spu_info[spu->number].spu->local_store;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002741 }
2742}
2743
2744static void stop_spus(void)
2745{
2746 struct spu *spu;
2747 int i;
2748 u64 tmp;
2749
2750 for (i = 0; i < XMON_NUM_SPUS; i++) {
2751 if (!spu_info[i].spu)
2752 continue;
2753
2754 if (setjmp(bus_error_jmp) == 0) {
2755 catch_memory_errors = 1;
2756 sync();
2757
2758 spu = spu_info[i].spu;
2759
2760 spu_info[i].saved_spu_runcntl_RW =
2761 in_be32(&spu->problem->spu_runcntl_RW);
2762
2763 tmp = spu_mfc_sr1_get(spu);
2764 spu_info[i].saved_mfc_sr1_RW = tmp;
2765
2766 tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
2767 spu_mfc_sr1_set(spu, tmp);
2768
2769 sync();
2770 __delay(200);
2771
2772 spu_info[i].stopped_ok = 1;
Michael Ellerman2a144422006-11-23 00:46:40 +01002773
2774 printf("Stopped spu %.2d (was %s)\n", i,
2775 spu_info[i].saved_spu_runcntl_RW ?
2776 "running" : "stopped");
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002777 } else {
2778 catch_memory_errors = 0;
2779 printf("*** Error stopping spu %.2d\n", i);
2780 }
2781 catch_memory_errors = 0;
2782 }
2783}
2784
2785static void restart_spus(void)
2786{
2787 struct spu *spu;
2788 int i;
2789
2790 for (i = 0; i < XMON_NUM_SPUS; i++) {
2791 if (!spu_info[i].spu)
2792 continue;
2793
2794 if (!spu_info[i].stopped_ok) {
2795 printf("*** Error, spu %d was not successfully stopped"
2796 ", not restarting\n", i);
2797 continue;
2798 }
2799
2800 if (setjmp(bus_error_jmp) == 0) {
2801 catch_memory_errors = 1;
2802 sync();
2803
2804 spu = spu_info[i].spu;
2805 spu_mfc_sr1_set(spu, spu_info[i].saved_mfc_sr1_RW);
2806 out_be32(&spu->problem->spu_runcntl_RW,
2807 spu_info[i].saved_spu_runcntl_RW);
2808
2809 sync();
2810 __delay(200);
2811
2812 printf("Restarted spu %.2d\n", i);
2813 } else {
2814 catch_memory_errors = 0;
2815 printf("*** Error restarting spu %.2d\n", i);
2816 }
2817 catch_memory_errors = 0;
2818 }
2819}
2820
Michael Ellermana8984972006-10-24 18:31:28 +02002821#define DUMP_WIDTH 23
Michael Ellerman437a0702006-11-23 00:46:39 +01002822#define DUMP_VALUE(format, field, value) \
Michael Ellermana8984972006-10-24 18:31:28 +02002823do { \
2824 if (setjmp(bus_error_jmp) == 0) { \
2825 catch_memory_errors = 1; \
2826 sync(); \
2827 printf(" %-*s = "format"\n", DUMP_WIDTH, \
Michael Ellerman437a0702006-11-23 00:46:39 +01002828 #field, value); \
Michael Ellermana8984972006-10-24 18:31:28 +02002829 sync(); \
2830 __delay(200); \
2831 } else { \
2832 catch_memory_errors = 0; \
2833 printf(" %-*s = *** Error reading field.\n", \
2834 DUMP_WIDTH, #field); \
2835 } \
2836 catch_memory_errors = 0; \
2837} while (0)
2838
Michael Ellerman437a0702006-11-23 00:46:39 +01002839#define DUMP_FIELD(obj, format, field) \
2840 DUMP_VALUE(format, field, obj->field)
2841
Michael Ellermana8984972006-10-24 18:31:28 +02002842static void dump_spu_fields(struct spu *spu)
2843{
2844 printf("Dumping spu fields at address %p:\n", spu);
2845
2846 DUMP_FIELD(spu, "0x%x", number);
2847 DUMP_FIELD(spu, "%s", name);
Michael Ellermana8984972006-10-24 18:31:28 +02002848 DUMP_FIELD(spu, "0x%lx", local_store_phys);
2849 DUMP_FIELD(spu, "0x%p", local_store);
2850 DUMP_FIELD(spu, "0x%lx", ls_size);
2851 DUMP_FIELD(spu, "0x%x", node);
2852 DUMP_FIELD(spu, "0x%lx", flags);
Michael Ellermana8984972006-10-24 18:31:28 +02002853 DUMP_FIELD(spu, "%d", class_0_pending);
Luke Browningf3d69e02008-04-27 18:41:55 +00002854 DUMP_FIELD(spu, "0x%lx", class_0_dar);
Luke Browningf3d69e02008-04-27 18:41:55 +00002855 DUMP_FIELD(spu, "0x%lx", class_1_dar);
2856 DUMP_FIELD(spu, "0x%lx", class_1_dsisr);
Michael Ellermana8984972006-10-24 18:31:28 +02002857 DUMP_FIELD(spu, "0x%lx", irqs[0]);
2858 DUMP_FIELD(spu, "0x%lx", irqs[1]);
2859 DUMP_FIELD(spu, "0x%lx", irqs[2]);
2860 DUMP_FIELD(spu, "0x%x", slb_replace);
2861 DUMP_FIELD(spu, "%d", pid);
Michael Ellermana8984972006-10-24 18:31:28 +02002862 DUMP_FIELD(spu, "0x%p", mm);
2863 DUMP_FIELD(spu, "0x%p", ctx);
2864 DUMP_FIELD(spu, "0x%p", rq);
2865 DUMP_FIELD(spu, "0x%p", timestamp);
2866 DUMP_FIELD(spu, "0x%lx", problem_phys);
2867 DUMP_FIELD(spu, "0x%p", problem);
Michael Ellerman437a0702006-11-23 00:46:39 +01002868 DUMP_VALUE("0x%x", problem->spu_runcntl_RW,
2869 in_be32(&spu->problem->spu_runcntl_RW));
2870 DUMP_VALUE("0x%x", problem->spu_status_R,
2871 in_be32(&spu->problem->spu_status_R));
2872 DUMP_VALUE("0x%x", problem->spu_npc_RW,
2873 in_be32(&spu->problem->spu_npc_RW));
Michael Ellermana8984972006-10-24 18:31:28 +02002874 DUMP_FIELD(spu, "0x%p", priv2);
Michael Ellermana9852392006-11-23 00:46:50 +01002875 DUMP_FIELD(spu, "0x%p", pdata);
Michael Ellermana8984972006-10-24 18:31:28 +02002876}
2877
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002878int
2879spu_inst_dump(unsigned long adr, long count, int praddr)
2880{
2881 return generic_inst_dump(adr, count, praddr, print_insn_spu);
2882}
2883
2884static void dump_spu_ls(unsigned long num, int subcmd)
Michael Ellerman24a24c82006-11-23 00:46:41 +01002885{
2886 unsigned long offset, addr, ls_addr;
2887
2888 if (setjmp(bus_error_jmp) == 0) {
2889 catch_memory_errors = 1;
2890 sync();
2891 ls_addr = (unsigned long)spu_info[num].spu->local_store;
2892 sync();
2893 __delay(200);
2894 } else {
2895 catch_memory_errors = 0;
2896 printf("*** Error: accessing spu info for spu %d\n", num);
2897 return;
2898 }
2899 catch_memory_errors = 0;
2900
2901 if (scanhex(&offset))
2902 addr = ls_addr + offset;
2903 else
2904 addr = spu_info[num].dump_addr;
2905
2906 if (addr >= ls_addr + LS_SIZE) {
2907 printf("*** Error: address outside of local store\n");
2908 return;
2909 }
2910
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002911 switch (subcmd) {
2912 case 'i':
2913 addr += spu_inst_dump(addr, 16, 1);
2914 last_cmd = "sdi\n";
2915 break;
2916 default:
2917 prdump(addr, 64);
2918 addr += 64;
2919 last_cmd = "sd\n";
2920 break;
2921 }
Michael Ellerman24a24c82006-11-23 00:46:41 +01002922
2923 spu_info[num].dump_addr = addr;
2924}
2925
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002926static int do_spu_cmd(void)
2927{
Michael Ellerman24a24c82006-11-23 00:46:41 +01002928 static unsigned long num = 0;
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002929 int cmd, subcmd = 0;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002930
2931 cmd = inchar();
2932 switch (cmd) {
2933 case 's':
2934 stop_spus();
2935 break;
2936 case 'r':
2937 restart_spus();
2938 break;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002939 case 'd':
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002940 subcmd = inchar();
2941 if (isxdigit(subcmd) || subcmd == '\n')
2942 termch = subcmd;
2943 case 'f':
Michael Ellerman24a24c82006-11-23 00:46:41 +01002944 scanhex(&num);
2945 if (num >= XMON_NUM_SPUS || !spu_info[num].spu) {
Michael Ellermana8984972006-10-24 18:31:28 +02002946 printf("*** Error: invalid spu number\n");
Michael Ellerman24a24c82006-11-23 00:46:41 +01002947 return 0;
2948 }
2949
2950 switch (cmd) {
2951 case 'f':
2952 dump_spu_fields(spu_info[num].spu);
2953 break;
2954 default:
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002955 dump_spu_ls(num, subcmd);
Michael Ellerman24a24c82006-11-23 00:46:41 +01002956 break;
2957 }
2958
Michael Ellermana8984972006-10-24 18:31:28 +02002959 break;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002960 default:
2961 return -1;
2962 }
2963
2964 return 0;
2965}
Arnd Bergmanne0555952006-11-27 19:18:55 +01002966#else /* ! CONFIG_SPU_BASE */
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002967static int do_spu_cmd(void)
2968{
2969 return -1;
2970}
2971#endif