blob: 8bad7d5f32afb696e8cb0c9e225b3d62913df856 [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>
Anton Vorontsov322b4392008-12-17 10:08:55 +000044#include <asm/reg.h>
Paul Mackerrasf78541d2005-10-28 22:53:37 +100045
46#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#include <asm/hvcall.h>
Paul Mackerrasf78541d2005-10-28 22:53:37 +100048#include <asm/paca.h>
49#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
51#include "nonstdio.h"
Michael Ellermane0426042006-11-23 00:46:45 +010052#include "dis-asm.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54#define scanhex xmon_scanhex
55#define skipbl xmon_skipbl
56
57#ifdef CONFIG_SMP
Michael Ellerman1c8950f2008-05-08 14:27:17 +100058static cpumask_t cpus_in_xmon = CPU_MASK_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059static unsigned long xmon_taken = 1;
60static int xmon_owner;
61static int xmon_gate;
62#endif /* CONFIG_SMP */
63
Anton Blanchard5be34922010-01-12 00:50:14 +000064static unsigned long in_xmon __read_mostly = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66static unsigned long adrs;
67static int size = 1;
68#define MAX_DUMP (128 * 1024)
69static unsigned long ndump = 64;
70static unsigned long nidump = 16;
71static unsigned long ncsum = 4096;
72static int termch;
73static char tmpstr[128];
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static long bus_error_jmp[JMP_BUF_LEN];
76static int catch_memory_errors;
77static long *xmon_fault_jmp[NR_CPUS];
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
79/* Breakpoint stuff */
80struct bpt {
81 unsigned long address;
82 unsigned int instr[2];
83 atomic_t ref_count;
84 int enabled;
85 unsigned long pad;
86};
87
88/* Bits in bpt.enabled */
89#define BP_IABR_TE 1 /* IABR translation enabled */
90#define BP_IABR 2
91#define BP_TRAP 8
92#define BP_DABR 0x10
93
94#define NBPTS 256
95static struct bpt bpts[NBPTS];
96static struct bpt dabr;
97static struct bpt *iabr;
98static unsigned bpinstr = 0x7fe00008; /* trap */
99
100#define BP_NUM(bp) ((bp) - bpts + 1)
101
102/* Prototypes */
103static int cmds(struct pt_regs *);
104static int mread(unsigned long, void *, int);
105static int mwrite(unsigned long, void *, int);
106static int handle_fault(struct pt_regs *);
107static void byterev(unsigned char *, int);
108static void memex(void);
109static int bsesc(void);
110static void dump(void);
111static void prdump(unsigned long, long);
112static int ppc_inst_dump(unsigned long, long, int);
Vinay Sridharf312deb2009-05-14 23:13:07 +0000113static void dump_log_buf(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114static void backtrace(struct pt_regs *);
115static void excprint(struct pt_regs *);
116static void prregs(struct pt_regs *);
117static void memops(int);
118static void memlocate(void);
119static void memzcan(void);
120static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned);
121int skipbl(void);
122int scanhex(unsigned long *valp);
123static void scannl(void);
124static int hexdigit(int);
125void getstring(char *, int);
126static void flush_input(void);
127static int inchar(void);
128static void take_input(char *);
129static unsigned long read_spr(int);
130static void write_spr(int, unsigned long);
131static void super_regs(void);
132static void remove_bpts(void);
133static void insert_bpts(void);
134static void remove_cpu_bpts(void);
135static void insert_cpu_bpts(void);
136static struct bpt *at_breakpoint(unsigned long pc);
137static struct bpt *in_breakpoint_table(unsigned long pc, unsigned long *offp);
138static int do_step(struct pt_regs *);
139static void bpt_cmds(void);
140static void cacheflush(void);
141static int cpu_cmd(void);
142static void csum(void);
143static void bootcmds(void);
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000144static void proccall(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145void dump_segments(void);
146static void symbol_lookup(void);
Olaf Hering26c8af52006-09-08 16:29:21 +0200147static void xmon_show_stack(unsigned long sp, unsigned long lr,
148 unsigned long pc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149static void xmon_print_symbol(unsigned long address, const char *mid,
150 const char *after);
151static const char *getvecname(unsigned long vec);
152
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200153static int do_spu_cmd(void);
154
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100155#ifdef CONFIG_44x
156static void dump_tlb_44x(void);
157#endif
158
Michael Ellerman9f1067c22008-05-08 14:27:16 +1000159static int xmon_no_auto_backtrace;
Olaf Hering26c8af52006-09-08 16:29:21 +0200160
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000161extern void xmon_enter(void);
162extern void xmon_leave(void);
163
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000164#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\
Vinay Sridharf312deb2009-05-14 23:13:07 +0000201 dl dump the kernel log buffer\n\
Olaf Hering7e5b5932006-03-08 20:40:28 +0100202 dr dump stream of raw bytes\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 e print exception information\n\
204 f flush cache\n\
205 la lookup symbol+offset of specified address\n\
206 ls lookup address of specified symbol\n\
207 m examine/change memory\n\
208 mm move a block of memory\n\
209 ms set a block of memory\n\
210 md compare two blocks of memory\n\
211 ml locate a block of memory\n\
212 mz zero a block of memory\n\
213 mi show information about memory allocation\n\
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000214 p call a procedure\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 r print registers\n\
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200216 s single step\n"
Arnd Bergmanne0555952006-11-27 19:18:55 +0100217#ifdef CONFIG_SPU_BASE
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200218" ss stop execution on all spus\n\
Michael Ellermana8984972006-10-24 18:31:28 +0200219 sr restore execution on stopped spus\n\
Michael Ellerman24a24c82006-11-23 00:46:41 +0100220 sf # dump spu fields for spu # (in hex)\n\
Michael Ellermanc99176a2007-02-26 18:14:06 +0900221 sd # dump spu local store for spu # (in hex)\n\
Michael Ellermanaf89fb82006-11-23 00:46:44 +0100222 sdi # disassemble spu local store for spu # (in hex)\n"
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200223#endif
224" S print special registers\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 t print backtrace\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 x exit monitor and recover\n\
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000227 X exit monitor and dont recover\n"
228#ifdef CONFIG_PPC64
229" u dump segment table or SLB\n"
230#endif
231#ifdef CONFIG_PPC_STD_MMU_32
232" u dump segment registers\n"
233#endif
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100234#ifdef CONFIG_44x
235" u dump TLB\n"
236#endif
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000237" ? help\n"
238" zr reboot\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 zh halt\n"
240;
241
242static struct pt_regs *xmon_regs;
243
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000244static inline void sync(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
246 asm volatile("sync; isync");
247}
248
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000249static inline void store_inst(void *p)
250{
251 asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p));
252}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000254static inline void cflush(void *p)
255{
256 asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p));
257}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000259static inline void cinval(void *p)
260{
261 asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p));
262}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264/*
265 * Disable surveillance (the service processor watchdog function)
266 * while we are in xmon.
267 * XXX we should re-enable it when we leave. :)
268 */
269#define SURVEILLANCE_TOKEN 9000
270
271static inline void disable_surveillance(void)
272{
273#ifdef CONFIG_PPC_PSERIES
274 /* Since this can't be a module, args should end up below 4GB. */
275 static struct rtas_args args;
276
277 /*
278 * At this point we have got all the cpus we can into
279 * xmon, so there is hopefully no other cpu calling RTAS
280 * at the moment, even though we don't take rtas.lock.
281 * If we did try to take rtas.lock there would be a
282 * real possibility of deadlock.
283 */
284 args.token = rtas_token("set-indicator");
285 if (args.token == RTAS_UNKNOWN_SERVICE)
286 return;
287 args.nargs = 3;
288 args.nret = 1;
289 args.rets = &args.args[3];
290 args.args[0] = SURVEILLANCE_TOKEN;
291 args.args[1] = 0;
292 args.args[2] = 0;
293 enter_rtas(__pa(&args));
294#endif /* CONFIG_PPC_PSERIES */
295}
296
297#ifdef CONFIG_SMP
298static int xmon_speaker;
299
300static void get_output_lock(void)
301{
302 int me = smp_processor_id() + 0x100;
303 int last_speaker = 0, prev;
304 long timeout;
305
306 if (xmon_speaker == me)
307 return;
308 for (;;) {
309 if (xmon_speaker == 0) {
310 last_speaker = cmpxchg(&xmon_speaker, 0, me);
311 if (last_speaker == 0)
312 return;
313 }
314 timeout = 10000000;
315 while (xmon_speaker == last_speaker) {
316 if (--timeout > 0)
317 continue;
318 /* hostile takeover */
319 prev = cmpxchg(&xmon_speaker, last_speaker, me);
320 if (prev == last_speaker)
321 return;
322 break;
323 }
324 }
325}
326
327static void release_output_lock(void)
328{
329 xmon_speaker = 0;
330}
Michael Ellerman1c8950f2008-05-08 14:27:17 +1000331
332int cpus_are_in_xmon(void)
333{
334 return !cpus_empty(cpus_in_xmon);
335}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336#endif
337
Josh Boyerdaf8f402009-09-23 03:51:04 +0000338static inline int unrecoverable_excp(struct pt_regs *regs)
339{
340#ifdef CONFIG_4xx
341 /* We have no MSR_RI bit on 4xx, so we simply return false */
342 return 0;
343#else
344 return ((regs->msr & MSR_RI) == 0);
345#endif
346}
347
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000348static int xmon_core(struct pt_regs *regs, int fromipi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349{
350 int cmd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 struct bpt *bp;
352 long recurse_jmp[JMP_BUF_LEN];
353 unsigned long offset;
Anton Blanchardf13659e2007-03-21 01:48:34 +1100354 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355#ifdef CONFIG_SMP
356 int cpu;
357 int secondary;
358 unsigned long timeout;
359#endif
360
Anton Blanchardf13659e2007-03-21 01:48:34 +1100361 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
363 bp = in_breakpoint_table(regs->nip, &offset);
364 if (bp != NULL) {
365 regs->nip = bp->address + offset;
366 atomic_dec(&bp->ref_count);
367 }
368
369 remove_cpu_bpts();
370
371#ifdef CONFIG_SMP
372 cpu = smp_processor_id();
373 if (cpu_isset(cpu, cpus_in_xmon)) {
374 get_output_lock();
375 excprint(regs);
376 printf("cpu 0x%x: Exception %lx %s in xmon, "
377 "returning to main loop\n",
378 cpu, regs->trap, getvecname(TRAP(regs)));
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000379 release_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 longjmp(xmon_fault_jmp[cpu], 1);
381 }
382
383 if (setjmp(recurse_jmp) != 0) {
384 if (!in_xmon || !xmon_gate) {
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000385 get_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 printf("xmon: WARNING: bad recursive fault "
387 "on cpu 0x%x\n", cpu);
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000388 release_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 goto waiting;
390 }
391 secondary = !(xmon_taken && cpu == xmon_owner);
392 goto cmdloop;
393 }
394
395 xmon_fault_jmp[cpu] = recurse_jmp;
396 cpu_set(cpu, cpus_in_xmon);
397
398 bp = NULL;
399 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF))
400 bp = at_breakpoint(regs->nip);
Josh Boyerdaf8f402009-09-23 03:51:04 +0000401 if (bp || unrecoverable_excp(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 fromipi = 0;
403
404 if (!fromipi) {
405 get_output_lock();
406 excprint(regs);
407 if (bp) {
408 printf("cpu 0x%x stopped at breakpoint 0x%x (",
409 cpu, BP_NUM(bp));
410 xmon_print_symbol(regs->nip, " ", ")\n");
411 }
Josh Boyerdaf8f402009-09-23 03:51:04 +0000412 if (unrecoverable_excp(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 printf("WARNING: exception is not recoverable, "
414 "can't continue\n");
415 release_output_lock();
416 }
417
418 waiting:
419 secondary = 1;
420 while (secondary && !xmon_gate) {
421 if (in_xmon == 0) {
422 if (fromipi)
423 goto leave;
424 secondary = test_and_set_bit(0, &in_xmon);
425 }
426 barrier();
427 }
428
429 if (!secondary && !xmon_gate) {
430 /* we are the first cpu to come in */
431 /* interrupt other cpu(s) */
432 int ncpus = num_online_cpus();
433
434 xmon_owner = cpu;
435 mb();
436 if (ncpus > 1) {
437 smp_send_debugger_break(MSG_ALL_BUT_SELF);
438 /* wait for other cpus to come in */
439 for (timeout = 100000000; timeout != 0; --timeout) {
440 if (cpus_weight(cpus_in_xmon) >= ncpus)
441 break;
442 barrier();
443 }
444 }
445 remove_bpts();
446 disable_surveillance();
447 /* for breakpoint or single step, print the current instr. */
448 if (bp || TRAP(regs) == 0xd00)
449 ppc_inst_dump(regs->nip, 1, 0);
450 printf("enter ? for help\n");
451 mb();
452 xmon_gate = 1;
453 barrier();
454 }
455
456 cmdloop:
457 while (in_xmon) {
458 if (secondary) {
459 if (cpu == xmon_owner) {
460 if (!test_and_set_bit(0, &xmon_taken)) {
461 secondary = 0;
462 continue;
463 }
464 /* missed it */
465 while (cpu == xmon_owner)
466 barrier();
467 }
468 barrier();
469 } else {
470 cmd = cmds(regs);
471 if (cmd != 0) {
472 /* exiting xmon */
473 insert_bpts();
474 xmon_gate = 0;
475 wmb();
476 in_xmon = 0;
477 break;
478 }
479 /* have switched to some other cpu */
480 secondary = 1;
481 }
482 }
483 leave:
484 cpu_clear(cpu, cpus_in_xmon);
485 xmon_fault_jmp[cpu] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486#else
487 /* UP is simple... */
488 if (in_xmon) {
489 printf("Exception %lx %s in xmon, returning to main loop\n",
490 regs->trap, getvecname(TRAP(regs)));
491 longjmp(xmon_fault_jmp[0], 1);
492 }
493 if (setjmp(recurse_jmp) == 0) {
494 xmon_fault_jmp[0] = recurse_jmp;
495 in_xmon = 1;
496
497 excprint(regs);
498 bp = at_breakpoint(regs->nip);
499 if (bp) {
500 printf("Stopped at breakpoint %x (", BP_NUM(bp));
501 xmon_print_symbol(regs->nip, " ", ")\n");
502 }
Josh Boyerdaf8f402009-09-23 03:51:04 +0000503 if (unrecoverable_excp(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 printf("WARNING: exception is not recoverable, "
505 "can't continue\n");
506 remove_bpts();
507 disable_surveillance();
508 /* for breakpoint or single step, print the current instr. */
509 if (bp || TRAP(regs) == 0xd00)
510 ppc_inst_dump(regs->nip, 1, 0);
511 printf("enter ? for help\n");
512 }
513
514 cmd = cmds(regs);
515
516 insert_bpts();
517 in_xmon = 0;
518#endif
519
Josh Boyercdd39042009-10-05 04:46:05 +0000520#ifdef CONFIG_BOOKE
521 if (regs->msr & MSR_DE) {
522 bp = at_breakpoint(regs->nip);
523 if (bp != NULL) {
524 regs->nip = (unsigned long) &bp->instr[0];
525 atomic_inc(&bp->ref_count);
526 }
527 }
528#else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
530 bp = at_breakpoint(regs->nip);
531 if (bp != NULL) {
532 int stepped = emulate_step(regs, bp->instr[0]);
533 if (stepped == 0) {
534 regs->nip = (unsigned long) &bp->instr[0];
535 atomic_inc(&bp->ref_count);
536 } else if (stepped < 0) {
537 printf("Couldn't single-step %s instruction\n",
538 (IS_RFID(bp->instr[0])? "rfid": "mtmsrd"));
539 }
540 }
541 }
Josh Boyercdd39042009-10-05 04:46:05 +0000542#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 insert_cpu_bpts();
544
Anton Blanchardf13659e2007-03-21 01:48:34 +1100545 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Paul Mackerras0a730ae2006-10-03 21:32:49 +1000547 return cmd != 'X' && cmd != EOF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548}
549
550int xmon(struct pt_regs *excp)
551{
552 struct pt_regs regs;
553
554 if (excp == NULL) {
Anton Vorontsov322b4392008-12-17 10:08:55 +0000555 ppc_save_regs(&regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 excp = &regs;
557 }
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 return xmon_core(excp, 0);
560}
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000561EXPORT_SYMBOL(xmon);
562
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000563irqreturn_t xmon_irq(int irq, void *d)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000564{
565 unsigned long flags;
566 local_irq_save(flags);
567 printf("Keyboard interrupt\n");
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000568 xmon(get_irq_regs());
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000569 local_irq_restore(flags);
570 return IRQ_HANDLED;
571}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000573static int xmon_bpt(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574{
575 struct bpt *bp;
576 unsigned long offset;
577
578 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
579 return 0;
580
581 /* Are we at the trap at bp->instr[1] for some bp? */
582 bp = in_breakpoint_table(regs->nip, &offset);
583 if (bp != NULL && offset == 4) {
584 regs->nip = bp->address + 4;
585 atomic_dec(&bp->ref_count);
586 return 1;
587 }
588
589 /* Are we at a breakpoint? */
590 bp = at_breakpoint(regs->nip);
591 if (!bp)
592 return 0;
593
594 xmon_core(regs, 0);
595
596 return 1;
597}
598
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000599static int xmon_sstep(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600{
601 if (user_mode(regs))
602 return 0;
603 xmon_core(regs, 0);
604 return 1;
605}
606
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000607static int xmon_dabr_match(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608{
609 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
610 return 0;
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000611 if (dabr.enabled == 0)
612 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 xmon_core(regs, 0);
614 return 1;
615}
616
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000617static int xmon_iabr_match(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618{
619 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
620 return 0;
Michael Ellerman9f1067c22008-05-08 14:27:16 +1000621 if (iabr == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 return 0;
623 xmon_core(regs, 0);
624 return 1;
625}
626
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000627static int xmon_ipi(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628{
629#ifdef CONFIG_SMP
630 if (in_xmon && !cpu_isset(smp_processor_id(), cpus_in_xmon))
631 xmon_core(regs, 1);
632#endif
633 return 0;
634}
635
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000636static int xmon_fault_handler(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637{
638 struct bpt *bp;
639 unsigned long offset;
640
641 if (in_xmon && catch_memory_errors)
642 handle_fault(regs); /* doesn't return */
643
644 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
645 bp = in_breakpoint_table(regs->nip, &offset);
646 if (bp != NULL) {
647 regs->nip = bp->address + offset;
648 atomic_dec(&bp->ref_count);
649 }
650 }
651
652 return 0;
653}
654
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655static struct bpt *at_breakpoint(unsigned long pc)
656{
657 int i;
658 struct bpt *bp;
659
660 bp = bpts;
661 for (i = 0; i < NBPTS; ++i, ++bp)
662 if (bp->enabled && pc == bp->address)
663 return bp;
664 return NULL;
665}
666
667static struct bpt *in_breakpoint_table(unsigned long nip, unsigned long *offp)
668{
669 unsigned long off;
670
671 off = nip - (unsigned long) bpts;
672 if (off >= sizeof(bpts))
673 return NULL;
674 off %= sizeof(struct bpt);
675 if (off != offsetof(struct bpt, instr[0])
676 && off != offsetof(struct bpt, instr[1]))
677 return NULL;
678 *offp = off - offsetof(struct bpt, instr[0]);
679 return (struct bpt *) (nip - off);
680}
681
682static struct bpt *new_breakpoint(unsigned long a)
683{
684 struct bpt *bp;
685
686 a &= ~3UL;
687 bp = at_breakpoint(a);
688 if (bp)
689 return bp;
690
691 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
692 if (!bp->enabled && atomic_read(&bp->ref_count) == 0) {
693 bp->address = a;
694 bp->instr[1] = bpinstr;
695 store_inst(&bp->instr[1]);
696 return bp;
697 }
698 }
699
700 printf("Sorry, no free breakpoints. Please clear one first.\n");
701 return NULL;
702}
703
704static void insert_bpts(void)
705{
706 int i;
707 struct bpt *bp;
708
709 bp = bpts;
710 for (i = 0; i < NBPTS; ++i, ++bp) {
711 if ((bp->enabled & (BP_TRAP|BP_IABR)) == 0)
712 continue;
713 if (mread(bp->address, &bp->instr[0], 4) != 4) {
714 printf("Couldn't read instruction at %lx, "
715 "disabling breakpoint there\n", bp->address);
716 bp->enabled = 0;
717 continue;
718 }
719 if (IS_MTMSRD(bp->instr[0]) || IS_RFID(bp->instr[0])) {
720 printf("Breakpoint at %lx is on an mtmsrd or rfid "
721 "instruction, disabling it\n", bp->address);
722 bp->enabled = 0;
723 continue;
724 }
725 store_inst(&bp->instr[0]);
726 if (bp->enabled & BP_IABR)
727 continue;
728 if (mwrite(bp->address, &bpinstr, 4) != 4) {
729 printf("Couldn't write instruction at %lx, "
730 "disabling breakpoint there\n", bp->address);
731 bp->enabled &= ~BP_TRAP;
732 continue;
733 }
734 store_inst((void *)bp->address);
735 }
736}
737
738static void insert_cpu_bpts(void)
739{
740 if (dabr.enabled)
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000741 set_dabr(dabr.address | (dabr.enabled & 7));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 if (iabr && cpu_has_feature(CPU_FTR_IABR))
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000743 mtspr(SPRN_IABR, iabr->address
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
745}
746
747static void remove_bpts(void)
748{
749 int i;
750 struct bpt *bp;
751 unsigned instr;
752
753 bp = bpts;
754 for (i = 0; i < NBPTS; ++i, ++bp) {
755 if ((bp->enabled & (BP_TRAP|BP_IABR)) != BP_TRAP)
756 continue;
757 if (mread(bp->address, &instr, 4) == 4
758 && instr == bpinstr
759 && mwrite(bp->address, &bp->instr, 4) != 4)
760 printf("Couldn't remove breakpoint at %lx\n",
761 bp->address);
762 else
763 store_inst((void *)bp->address);
764 }
765}
766
767static void remove_cpu_bpts(void)
768{
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000769 set_dabr(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 if (cpu_has_feature(CPU_FTR_IABR))
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000771 mtspr(SPRN_IABR, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772}
773
774/* Command interpreting routine */
775static char *last_cmd;
776
777static int
778cmds(struct pt_regs *excp)
779{
780 int cmd = 0;
781
782 last_cmd = NULL;
783 xmon_regs = excp;
Olaf Hering26c8af52006-09-08 16:29:21 +0200784
785 if (!xmon_no_auto_backtrace) {
786 xmon_no_auto_backtrace = 1;
787 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
788 }
789
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 for(;;) {
791#ifdef CONFIG_SMP
792 printf("%x:", smp_processor_id());
793#endif /* CONFIG_SMP */
794 printf("mon> ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 flush_input();
796 termch = 0;
797 cmd = skipbl();
798 if( cmd == '\n' ) {
799 if (last_cmd == NULL)
800 continue;
801 take_input(last_cmd);
802 last_cmd = NULL;
803 cmd = inchar();
804 }
805 switch (cmd) {
806 case 'm':
807 cmd = inchar();
808 switch (cmd) {
809 case 'm':
810 case 's':
811 case 'd':
812 memops(cmd);
813 break;
814 case 'l':
815 memlocate();
816 break;
817 case 'z':
818 memzcan();
819 break;
820 case 'i':
821 show_mem();
822 break;
823 default:
824 termch = cmd;
825 memex();
826 }
827 break;
828 case 'd':
829 dump();
830 break;
831 case 'l':
832 symbol_lookup();
833 break;
834 case 'r':
835 prregs(excp); /* print regs */
836 break;
837 case 'e':
838 excprint(excp);
839 break;
840 case 'S':
841 super_regs();
842 break;
843 case 't':
844 backtrace(excp);
845 break;
846 case 'f':
847 cacheflush();
848 break;
849 case 's':
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200850 if (do_spu_cmd() == 0)
851 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 if (do_step(excp))
853 return cmd;
854 break;
855 case 'x':
856 case 'X':
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100857 return cmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 case EOF:
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100859 printf(" <no input ...>\n");
860 mdelay(2000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 return cmd;
862 case '?':
Ishizaki Kou4d404ed2007-07-18 19:26:40 +1000863 xmon_puts(help_string);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 case 'b':
866 bpt_cmds();
867 break;
868 case 'C':
869 csum();
870 break;
871 case 'c':
872 if (cpu_cmd())
873 return 0;
874 break;
875 case 'z':
876 bootcmds();
877 break;
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000878 case 'p':
879 proccall();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 break;
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000881#ifdef CONFIG_PPC_STD_MMU
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 case 'u':
883 dump_segments();
884 break;
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000885#endif
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100886#ifdef CONFIG_4xx
887 case 'u':
888 dump_tlb_44x();
889 break;
890#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 default:
892 printf("Unrecognized command: ");
893 do {
894 if (' ' < cmd && cmd <= '~')
895 putchar(cmd);
896 else
897 printf("\\x%x", cmd);
898 cmd = inchar();
899 } while (cmd != '\n');
900 printf(" (type ? for help)\n");
901 break;
902 }
903 }
904}
905
Josh Boyercdd39042009-10-05 04:46:05 +0000906#ifdef CONFIG_BOOKE
907static int do_step(struct pt_regs *regs)
908{
909 regs->msr |= MSR_DE;
910 mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC | DBCR0_IDM);
911 return 1;
912}
913#else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914/*
915 * Step a single instruction.
916 * Some instructions we emulate, others we execute with MSR_SE set.
917 */
918static int do_step(struct pt_regs *regs)
919{
920 unsigned int instr;
921 int stepped;
922
923 /* check we are in 64-bit kernel mode, translation enabled */
924 if ((regs->msr & (MSR_SF|MSR_PR|MSR_IR)) == (MSR_SF|MSR_IR)) {
925 if (mread(regs->nip, &instr, 4) == 4) {
926 stepped = emulate_step(regs, instr);
927 if (stepped < 0) {
928 printf("Couldn't single-step %s instruction\n",
929 (IS_RFID(instr)? "rfid": "mtmsrd"));
930 return 0;
931 }
932 if (stepped > 0) {
933 regs->trap = 0xd00 | (regs->trap & 1);
934 printf("stepped to ");
935 xmon_print_symbol(regs->nip, " ", "\n");
936 ppc_inst_dump(regs->nip, 1, 0);
937 return 0;
938 }
939 }
940 }
941 regs->msr |= MSR_SE;
942 return 1;
943}
Josh Boyercdd39042009-10-05 04:46:05 +0000944#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945
946static void bootcmds(void)
947{
948 int cmd;
949
950 cmd = inchar();
951 if (cmd == 'r')
952 ppc_md.restart(NULL);
953 else if (cmd == 'h')
954 ppc_md.halt();
955 else if (cmd == 'p')
956 ppc_md.power_off();
957}
958
959static int cpu_cmd(void)
960{
961#ifdef CONFIG_SMP
962 unsigned long cpu;
963 int timeout;
964 int count;
965
966 if (!scanhex(&cpu)) {
967 /* print cpus waiting or in xmon */
968 printf("cpus stopped:");
969 count = 0;
970 for (cpu = 0; cpu < NR_CPUS; ++cpu) {
971 if (cpu_isset(cpu, cpus_in_xmon)) {
972 if (count == 0)
973 printf(" %x", cpu);
974 ++count;
975 } else {
976 if (count > 1)
977 printf("-%x", cpu - 1);
978 count = 0;
979 }
980 }
981 if (count > 1)
982 printf("-%x", NR_CPUS - 1);
983 printf("\n");
984 return 0;
985 }
986 /* try to switch to cpu specified */
987 if (!cpu_isset(cpu, cpus_in_xmon)) {
988 printf("cpu 0x%x isn't in xmon\n", cpu);
989 return 0;
990 }
991 xmon_taken = 0;
992 mb();
993 xmon_owner = cpu;
994 timeout = 10000000;
995 while (!xmon_taken) {
996 if (--timeout == 0) {
997 if (test_and_set_bit(0, &xmon_taken))
998 break;
999 /* take control back */
1000 mb();
1001 xmon_owner = smp_processor_id();
1002 printf("cpu %u didn't take control\n", cpu);
1003 return 0;
1004 }
1005 barrier();
1006 }
1007 return 1;
1008#else
1009 return 0;
1010#endif /* CONFIG_SMP */
1011}
1012
1013static unsigned short fcstab[256] = {
1014 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
1015 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
1016 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
1017 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
1018 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
1019 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
1020 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
1021 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
1022 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
1023 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
1024 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
1025 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
1026 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
1027 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
1028 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
1029 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
1030 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
1031 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
1032 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
1033 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
1034 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
1035 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
1036 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
1037 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
1038 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
1039 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
1040 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
1041 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
1042 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
1043 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
1044 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
1045 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
1046};
1047
1048#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
1049
1050static void
1051csum(void)
1052{
1053 unsigned int i;
1054 unsigned short fcs;
1055 unsigned char v;
1056
1057 if (!scanhex(&adrs))
1058 return;
1059 if (!scanhex(&ncsum))
1060 return;
1061 fcs = 0xffff;
1062 for (i = 0; i < ncsum; ++i) {
1063 if (mread(adrs+i, &v, 1) == 0) {
1064 printf("csum stopped at %x\n", adrs+i);
1065 break;
1066 }
1067 fcs = FCS(fcs, v);
1068 }
1069 printf("%x\n", fcs);
1070}
1071
1072/*
1073 * Check if this is a suitable place to put a breakpoint.
1074 */
1075static long check_bp_loc(unsigned long addr)
1076{
1077 unsigned int instr;
1078
1079 addr &= ~3;
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001080 if (!is_kernel_addr(addr)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 printf("Breakpoints may only be placed at kernel addresses\n");
1082 return 0;
1083 }
1084 if (!mread(addr, &instr, sizeof(instr))) {
1085 printf("Can't read instruction at address %lx\n", addr);
1086 return 0;
1087 }
1088 if (IS_MTMSRD(instr) || IS_RFID(instr)) {
1089 printf("Breakpoints may not be placed on mtmsrd or rfid "
1090 "instructions\n");
1091 return 0;
1092 }
1093 return 1;
1094}
1095
1096static char *breakpoint_help_string =
1097 "Breakpoint command usage:\n"
1098 "b show breakpoints\n"
1099 "b <addr> [cnt] set breakpoint at given instr addr\n"
1100 "bc clear all breakpoints\n"
1101 "bc <n/addr> clear breakpoint number n or at addr\n"
1102 "bi <addr> [cnt] set hardware instr breakpoint (POWER3/RS64 only)\n"
1103 "bd <addr> [cnt] set hardware data breakpoint\n"
1104 "";
1105
1106static void
1107bpt_cmds(void)
1108{
1109 int cmd;
1110 unsigned long a;
1111 int mode, i;
1112 struct bpt *bp;
1113 const char badaddr[] = "Only kernel addresses are permitted "
1114 "for breakpoints\n";
1115
1116 cmd = inchar();
1117 switch (cmd) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001118#ifndef CONFIG_8xx
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 case 'd': /* bd - hardware data breakpoint */
1120 mode = 7;
1121 cmd = inchar();
1122 if (cmd == 'r')
1123 mode = 5;
1124 else if (cmd == 'w')
1125 mode = 6;
1126 else
1127 termch = cmd;
1128 dabr.address = 0;
1129 dabr.enabled = 0;
1130 if (scanhex(&dabr.address)) {
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001131 if (!is_kernel_addr(dabr.address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 printf(badaddr);
1133 break;
1134 }
1135 dabr.address &= ~7;
1136 dabr.enabled = mode | BP_DABR;
1137 }
1138 break;
1139
1140 case 'i': /* bi - hardware instr breakpoint */
1141 if (!cpu_has_feature(CPU_FTR_IABR)) {
1142 printf("Hardware instruction breakpoint "
1143 "not supported on this cpu\n");
1144 break;
1145 }
1146 if (iabr) {
1147 iabr->enabled &= ~(BP_IABR | BP_IABR_TE);
1148 iabr = NULL;
1149 }
1150 if (!scanhex(&a))
1151 break;
1152 if (!check_bp_loc(a))
1153 break;
1154 bp = new_breakpoint(a);
1155 if (bp != NULL) {
1156 bp->enabled |= BP_IABR | BP_IABR_TE;
1157 iabr = bp;
1158 }
1159 break;
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001160#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161
1162 case 'c':
1163 if (!scanhex(&a)) {
1164 /* clear all breakpoints */
1165 for (i = 0; i < NBPTS; ++i)
1166 bpts[i].enabled = 0;
1167 iabr = NULL;
1168 dabr.enabled = 0;
1169 printf("All breakpoints cleared\n");
1170 break;
1171 }
1172
1173 if (a <= NBPTS && a >= 1) {
1174 /* assume a breakpoint number */
1175 bp = &bpts[a-1]; /* bp nums are 1 based */
1176 } else {
1177 /* assume a breakpoint address */
1178 bp = at_breakpoint(a);
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001179 if (bp == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 printf("No breakpoint at %x\n", a);
1181 break;
1182 }
1183 }
1184
1185 printf("Cleared breakpoint %x (", BP_NUM(bp));
1186 xmon_print_symbol(bp->address, " ", ")\n");
1187 bp->enabled = 0;
1188 break;
1189
1190 default:
1191 termch = cmd;
1192 cmd = skipbl();
1193 if (cmd == '?') {
1194 printf(breakpoint_help_string);
1195 break;
1196 }
1197 termch = cmd;
1198 if (!scanhex(&a)) {
1199 /* print all breakpoints */
1200 printf(" type address\n");
1201 if (dabr.enabled) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001202 printf(" data "REG" [", dabr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 if (dabr.enabled & 1)
1204 printf("r");
1205 if (dabr.enabled & 2)
1206 printf("w");
1207 printf("]\n");
1208 }
1209 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
1210 if (!bp->enabled)
1211 continue;
1212 printf("%2x %s ", BP_NUM(bp),
1213 (bp->enabled & BP_IABR)? "inst": "trap");
1214 xmon_print_symbol(bp->address, " ", "\n");
1215 }
1216 break;
1217 }
1218
1219 if (!check_bp_loc(a))
1220 break;
1221 bp = new_breakpoint(a);
1222 if (bp != NULL)
1223 bp->enabled |= BP_TRAP;
1224 break;
1225 }
1226}
1227
1228/* Very cheap human name for vector lookup. */
1229static
1230const char *getvecname(unsigned long vec)
1231{
1232 char *ret;
1233
1234 switch (vec) {
1235 case 0x100: ret = "(System Reset)"; break;
1236 case 0x200: ret = "(Machine Check)"; break;
1237 case 0x300: ret = "(Data Access)"; break;
1238 case 0x380: ret = "(Data SLB Access)"; break;
1239 case 0x400: ret = "(Instruction Access)"; break;
1240 case 0x480: ret = "(Instruction SLB Access)"; break;
1241 case 0x500: ret = "(Hardware Interrupt)"; break;
1242 case 0x600: ret = "(Alignment)"; break;
1243 case 0x700: ret = "(Program Check)"; break;
1244 case 0x800: ret = "(FPU Unavailable)"; break;
1245 case 0x900: ret = "(Decrementer)"; break;
1246 case 0xc00: ret = "(System Call)"; break;
1247 case 0xd00: ret = "(Single Step)"; break;
1248 case 0xf00: ret = "(Performance Monitor)"; break;
1249 case 0xf20: ret = "(Altivec Unavailable)"; break;
1250 case 0x1300: ret = "(Instruction Breakpoint)"; break;
1251 default: ret = "";
1252 }
1253 return ret;
1254}
1255
1256static void get_function_bounds(unsigned long pc, unsigned long *startp,
1257 unsigned long *endp)
1258{
1259 unsigned long size, offset;
1260 const char *name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
1262 *startp = *endp = 0;
1263 if (pc == 0)
1264 return;
1265 if (setjmp(bus_error_jmp) == 0) {
1266 catch_memory_errors = 1;
1267 sync();
Alexey Dobriyanffb45122007-05-08 00:28:41 -07001268 name = kallsyms_lookup(pc, &size, &offset, NULL, tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 if (name != NULL) {
1270 *startp = pc - offset;
1271 *endp = pc - offset + size;
1272 }
1273 sync();
1274 }
1275 catch_memory_errors = 0;
1276}
1277
1278static int xmon_depth_to_print = 64;
1279
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001280#define LRSAVE_OFFSET (STACK_FRAME_LR_SAVE * sizeof(unsigned long))
1281#define MARKER_OFFSET (STACK_FRAME_MARKER * sizeof(unsigned long))
1282
1283#ifdef __powerpc64__
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001284#define REGS_OFFSET 0x70
1285#else
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001286#define REGS_OFFSET 16
1287#endif
1288
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289static void xmon_show_stack(unsigned long sp, unsigned long lr,
1290 unsigned long pc)
1291{
1292 unsigned long ip;
1293 unsigned long newsp;
1294 unsigned long marker;
1295 int count = 0;
1296 struct pt_regs regs;
1297
1298 do {
1299 if (sp < PAGE_OFFSET) {
1300 if (sp != 0)
1301 printf("SP (%lx) is in userspace\n", sp);
1302 break;
1303 }
1304
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001305 if (!mread(sp + LRSAVE_OFFSET, &ip, sizeof(unsigned long))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 || !mread(sp, &newsp, sizeof(unsigned long))) {
1307 printf("Couldn't read stack frame at %lx\n", sp);
1308 break;
1309 }
1310
1311 /*
1312 * For the first stack frame, try to work out if
1313 * LR and/or the saved LR value in the bottommost
1314 * stack frame are valid.
1315 */
1316 if ((pc | lr) != 0) {
1317 unsigned long fnstart, fnend;
1318 unsigned long nextip;
1319 int printip = 1;
1320
1321 get_function_bounds(pc, &fnstart, &fnend);
1322 nextip = 0;
1323 if (newsp > sp)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001324 mread(newsp + LRSAVE_OFFSET, &nextip,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 sizeof(unsigned long));
1326 if (lr == ip) {
1327 if (lr < PAGE_OFFSET
1328 || (fnstart <= lr && lr < fnend))
1329 printip = 0;
1330 } else if (lr == nextip) {
1331 printip = 0;
1332 } else if (lr >= PAGE_OFFSET
1333 && !(fnstart <= lr && lr < fnend)) {
1334 printf("[link register ] ");
1335 xmon_print_symbol(lr, " ", "\n");
1336 }
1337 if (printip) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001338 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 xmon_print_symbol(ip, " ", " (unreliable)\n");
1340 }
1341 pc = lr = 0;
1342
1343 } else {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001344 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 xmon_print_symbol(ip, " ", "\n");
1346 }
1347
1348 /* Look for "regshere" marker to see if this is
1349 an exception frame. */
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001350 if (mread(sp + MARKER_OFFSET, &marker, sizeof(unsigned long))
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001351 && marker == STACK_FRAME_REGS_MARKER) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001352 if (mread(sp + REGS_OFFSET, &regs, sizeof(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 != sizeof(regs)) {
1354 printf("Couldn't read registers at %lx\n",
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001355 sp + REGS_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 break;
1357 }
1358 printf("--- Exception: %lx %s at ", regs.trap,
1359 getvecname(TRAP(&regs)));
1360 pc = regs.nip;
1361 lr = regs.link;
1362 xmon_print_symbol(pc, " ", "\n");
1363 }
1364
1365 if (newsp == 0)
1366 break;
1367
1368 sp = newsp;
1369 } while (count++ < xmon_depth_to_print);
1370}
1371
1372static void backtrace(struct pt_regs *excp)
1373{
1374 unsigned long sp;
1375
1376 if (scanhex(&sp))
1377 xmon_show_stack(sp, 0, 0);
1378 else
1379 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
1380 scannl();
1381}
1382
1383static void print_bug_trap(struct pt_regs *regs)
1384{
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001385#ifdef CONFIG_BUG
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001386 const struct bug_entry *bug;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 unsigned long addr;
1388
1389 if (regs->msr & MSR_PR)
1390 return; /* not in kernel */
1391 addr = regs->nip; /* address of trap instruction */
1392 if (addr < PAGE_OFFSET)
1393 return;
1394 bug = find_bug(regs->nip);
1395 if (bug == NULL)
1396 return;
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001397 if (is_warning_bug(bug))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 return;
1399
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001400#ifdef CONFIG_DEBUG_BUGVERBOSE
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001401 printf("kernel BUG at %s:%u!\n",
1402 bug->file, bug->line);
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001403#else
1404 printf("kernel BUG at %p!\n", (void *)bug->bug_addr);
1405#endif
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001406#endif /* CONFIG_BUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407}
1408
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001409static void excprint(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410{
1411 unsigned long trap;
1412
1413#ifdef CONFIG_SMP
1414 printf("cpu 0x%x: ", smp_processor_id());
1415#endif /* CONFIG_SMP */
1416
1417 trap = TRAP(fp);
1418 printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(trap), fp);
1419 printf(" pc: ");
1420 xmon_print_symbol(fp->nip, ": ", "\n");
1421
1422 printf(" lr: ", fp->link);
1423 xmon_print_symbol(fp->link, ": ", "\n");
1424
1425 printf(" sp: %lx\n", fp->gpr[1]);
1426 printf(" msr: %lx\n", fp->msr);
1427
1428 if (trap == 0x300 || trap == 0x380 || trap == 0x600) {
1429 printf(" dar: %lx\n", fp->dar);
1430 if (trap != 0x380)
1431 printf(" dsisr: %lx\n", fp->dsisr);
1432 }
1433
1434 printf(" current = 0x%lx\n", current);
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001435#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 printf(" paca = 0x%lx\n", get_paca());
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001437#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 if (current) {
1439 printf(" pid = %ld, comm = %s\n",
1440 current->pid, current->comm);
1441 }
1442
1443 if (trap == 0x700)
1444 print_bug_trap(fp);
1445}
1446
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001447static void prregs(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448{
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001449 int n, trap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 unsigned long base;
1451 struct pt_regs regs;
1452
1453 if (scanhex(&base)) {
1454 if (setjmp(bus_error_jmp) == 0) {
1455 catch_memory_errors = 1;
1456 sync();
1457 regs = *(struct pt_regs *)base;
1458 sync();
1459 __delay(200);
1460 } else {
1461 catch_memory_errors = 0;
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001462 printf("*** Error reading registers from "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 base);
1464 return;
1465 }
1466 catch_memory_errors = 0;
1467 fp = &regs;
1468 }
1469
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001470#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 if (FULL_REGS(fp)) {
1472 for (n = 0; n < 16; ++n)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001473 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 n, fp->gpr[n], n+16, fp->gpr[n+16]);
1475 } else {
1476 for (n = 0; n < 7; ++n)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001477 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 n, fp->gpr[n], n+7, fp->gpr[n+7]);
1479 }
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001480#else
1481 for (n = 0; n < 32; ++n) {
1482 printf("R%.2d = %.8x%s", n, fp->gpr[n],
1483 (n & 3) == 3? "\n": " ");
1484 if (n == 12 && !FULL_REGS(fp)) {
1485 printf("\n");
1486 break;
1487 }
1488 }
1489#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 printf("pc = ");
1491 xmon_print_symbol(fp->nip, " ", "\n");
1492 printf("lr = ");
1493 xmon_print_symbol(fp->link, " ", "\n");
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001494 printf("msr = "REG" cr = %.8lx\n", fp->msr, fp->ccr);
1495 printf("ctr = "REG" xer = "REG" trap = %4lx\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 fp->ctr, fp->xer, fp->trap);
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001497 trap = TRAP(fp);
1498 if (trap == 0x300 || trap == 0x380 || trap == 0x600)
1499 printf("dar = "REG" dsisr = %.8lx\n", fp->dar, fp->dsisr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500}
1501
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001502static void cacheflush(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503{
1504 int cmd;
1505 unsigned long nflush;
1506
1507 cmd = inchar();
1508 if (cmd != 'i')
1509 termch = cmd;
1510 scanhex((void *)&adrs);
1511 if (termch != '\n')
1512 termch = 0;
1513 nflush = 1;
1514 scanhex(&nflush);
1515 nflush = (nflush + L1_CACHE_BYTES - 1) / L1_CACHE_BYTES;
1516 if (setjmp(bus_error_jmp) == 0) {
1517 catch_memory_errors = 1;
1518 sync();
1519
1520 if (cmd != 'i') {
1521 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1522 cflush((void *) adrs);
1523 } else {
1524 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1525 cinval((void *) adrs);
1526 }
1527 sync();
1528 /* wait a little while to see if we get a machine check */
1529 __delay(200);
1530 }
1531 catch_memory_errors = 0;
1532}
1533
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001534static unsigned long
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535read_spr(int n)
1536{
1537 unsigned int instrs[2];
1538 unsigned long (*code)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 unsigned long ret = -1UL;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001540#ifdef CONFIG_PPC64
1541 unsigned long opd[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 opd[0] = (unsigned long)instrs;
1544 opd[1] = 0;
1545 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001546 code = (unsigned long (*)(void)) opd;
1547#else
1548 code = (unsigned long (*)(void)) instrs;
1549#endif
1550
1551 /* mfspr r3,n; blr */
1552 instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1553 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 store_inst(instrs);
1555 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
1557 if (setjmp(bus_error_jmp) == 0) {
1558 catch_memory_errors = 1;
1559 sync();
1560
1561 ret = code();
1562
1563 sync();
1564 /* wait a little while to see if we get a machine check */
1565 __delay(200);
1566 n = size;
1567 }
1568
1569 return ret;
1570}
1571
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001572static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573write_spr(int n, unsigned long val)
1574{
1575 unsigned int instrs[2];
1576 unsigned long (*code)(unsigned long);
Paul Mackerras548cceb2005-11-11 22:36:34 +11001577#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578 unsigned long opd[3];
1579
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 opd[0] = (unsigned long)instrs;
1581 opd[1] = 0;
1582 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001583 code = (unsigned long (*)(unsigned long)) opd;
1584#else
1585 code = (unsigned long (*)(unsigned long)) instrs;
1586#endif
1587
1588 instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1589 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 store_inst(instrs);
1591 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
1593 if (setjmp(bus_error_jmp) == 0) {
1594 catch_memory_errors = 1;
1595 sync();
1596
1597 code(val);
1598
1599 sync();
1600 /* wait a little while to see if we get a machine check */
1601 __delay(200);
1602 n = size;
1603 }
1604}
1605
1606static unsigned long regno;
1607extern char exc_prolog;
1608extern char dec_exc;
1609
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001610static void super_regs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611{
1612 int cmd;
1613 unsigned long val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614
1615 cmd = skipbl();
1616 if (cmd == '\n') {
1617 unsigned long sp, toc;
1618 asm("mr %0,1" : "=r" (sp) :);
1619 asm("mr %0,2" : "=r" (toc) :);
1620
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001621 printf("msr = "REG" sprg0= "REG"\n",
1622 mfmsr(), mfspr(SPRN_SPRG0));
1623 printf("pvr = "REG" sprg1= "REG"\n",
1624 mfspr(SPRN_PVR), mfspr(SPRN_SPRG1));
1625 printf("dec = "REG" sprg2= "REG"\n",
1626 mfspr(SPRN_DEC), mfspr(SPRN_SPRG2));
1627 printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3));
1628 printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629#ifdef CONFIG_PPC_ISERIES
Stephen Rothwell1d135812006-11-13 14:50:28 +11001630 if (firmware_has_feature(FW_FEATURE_ISERIES)) {
1631 struct paca_struct *ptrPaca;
1632 struct lppaca *ptrLpPaca;
Stephen Rothwell1d135812006-11-13 14:50:28 +11001633
1634 /* Dump out relevant Paca data areas. */
1635 printf("Paca: \n");
1636 ptrPaca = get_paca();
1637
1638 printf(" Local Processor Control Area (LpPaca): \n");
1639 ptrLpPaca = ptrPaca->lppaca_ptr;
1640 printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n",
1641 ptrLpPaca->saved_srr0, ptrLpPaca->saved_srr1);
1642 printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n",
1643 ptrLpPaca->saved_gpr3, ptrLpPaca->saved_gpr4);
Gautham R Shenoy69ddb572009-10-29 19:22:48 +00001644 printf(" Saved Gpr5=%.16lx \n",
1645 ptrLpPaca->gpr5_dword.saved_gpr5);
Stephen Rothwell1d135812006-11-13 14:50:28 +11001646 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647#endif
1648
1649 return;
1650 }
1651
1652 scanhex(&regno);
1653 switch (cmd) {
1654 case 'w':
1655 val = read_spr(regno);
1656 scanhex(&val);
1657 write_spr(regno, val);
1658 /* fall through */
1659 case 'r':
1660 printf("spr %lx = %lx\n", regno, read_spr(regno));
1661 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 }
1663 scannl();
1664}
1665
1666/*
1667 * Stuff for reading and writing memory safely
1668 */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001669static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670mread(unsigned long adrs, void *buf, int size)
1671{
1672 volatile int n;
1673 char *p, *q;
1674
1675 n = 0;
1676 if (setjmp(bus_error_jmp) == 0) {
1677 catch_memory_errors = 1;
1678 sync();
1679 p = (char *)adrs;
1680 q = (char *)buf;
1681 switch (size) {
1682 case 2:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001683 *(u16 *)q = *(u16 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 break;
1685 case 4:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001686 *(u32 *)q = *(u32 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 break;
1688 case 8:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001689 *(u64 *)q = *(u64 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 break;
1691 default:
1692 for( ; n < size; ++n) {
1693 *q++ = *p++;
1694 sync();
1695 }
1696 }
1697 sync();
1698 /* wait a little while to see if we get a machine check */
1699 __delay(200);
1700 n = size;
1701 }
1702 catch_memory_errors = 0;
1703 return n;
1704}
1705
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001706static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707mwrite(unsigned long adrs, void *buf, int size)
1708{
1709 volatile int n;
1710 char *p, *q;
1711
1712 n = 0;
1713 if (setjmp(bus_error_jmp) == 0) {
1714 catch_memory_errors = 1;
1715 sync();
1716 p = (char *) adrs;
1717 q = (char *) buf;
1718 switch (size) {
1719 case 2:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001720 *(u16 *)p = *(u16 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 break;
1722 case 4:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001723 *(u32 *)p = *(u32 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 break;
1725 case 8:
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001726 *(u64 *)p = *(u64 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 break;
1728 default:
1729 for ( ; n < size; ++n) {
1730 *p++ = *q++;
1731 sync();
1732 }
1733 }
1734 sync();
1735 /* wait a little while to see if we get a machine check */
1736 __delay(200);
1737 n = size;
1738 } else {
1739 printf("*** Error writing address %x\n", adrs + n);
1740 }
1741 catch_memory_errors = 0;
1742 return n;
1743}
1744
1745static int fault_type;
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001746static int fault_except;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747static char *fault_chars[] = { "--", "**", "##" };
1748
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001749static int handle_fault(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750{
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001751 fault_except = TRAP(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 switch (TRAP(regs)) {
1753 case 0x200:
1754 fault_type = 0;
1755 break;
1756 case 0x300:
1757 case 0x380:
1758 fault_type = 1;
1759 break;
1760 default:
1761 fault_type = 2;
1762 }
1763
1764 longjmp(bus_error_jmp, 1);
1765
1766 return 0;
1767}
1768
1769#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
1770
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001771static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772byterev(unsigned char *val, int size)
1773{
1774 int t;
1775
1776 switch (size) {
1777 case 2:
1778 SWAP(val[0], val[1], t);
1779 break;
1780 case 4:
1781 SWAP(val[0], val[3], t);
1782 SWAP(val[1], val[2], t);
1783 break;
1784 case 8: /* is there really any use for this? */
1785 SWAP(val[0], val[7], t);
1786 SWAP(val[1], val[6], t);
1787 SWAP(val[2], val[5], t);
1788 SWAP(val[3], val[4], t);
1789 break;
1790 }
1791}
1792
1793static int brev;
1794static int mnoread;
1795
1796static char *memex_help_string =
1797 "Memory examine command usage:\n"
1798 "m [addr] [flags] examine/change memory\n"
1799 " addr is optional. will start where left off.\n"
1800 " flags may include chars from this set:\n"
1801 " b modify by bytes (default)\n"
1802 " w modify by words (2 byte)\n"
1803 " l modify by longs (4 byte)\n"
1804 " d modify by doubleword (8 byte)\n"
1805 " r toggle reverse byte order mode\n"
1806 " n do not read memory (for i/o spaces)\n"
1807 " . ok to read (default)\n"
1808 "NOTE: flags are saved as defaults\n"
1809 "";
1810
1811static char *memex_subcmd_help_string =
1812 "Memory examine subcommands:\n"
1813 " hexval write this val to current location\n"
1814 " 'string' write chars from string to this location\n"
1815 " ' increment address\n"
1816 " ^ decrement address\n"
1817 " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n"
1818 " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n"
1819 " ` clear no-read flag\n"
1820 " ; stay at this addr\n"
1821 " v change to byte mode\n"
1822 " w change to word (2 byte) mode\n"
1823 " l change to long (4 byte) mode\n"
1824 " u change to doubleword (8 byte) mode\n"
1825 " m addr change current addr\n"
1826 " n toggle no-read flag\n"
1827 " r toggle byte reverse flag\n"
1828 " < count back up count bytes\n"
1829 " > count skip forward count bytes\n"
1830 " x exit this mode\n"
1831 "";
1832
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001833static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834memex(void)
1835{
1836 int cmd, inc, i, nslash;
1837 unsigned long n;
1838 unsigned char val[16];
1839
1840 scanhex((void *)&adrs);
1841 cmd = skipbl();
1842 if (cmd == '?') {
1843 printf(memex_help_string);
1844 return;
1845 } else {
1846 termch = cmd;
1847 }
1848 last_cmd = "m\n";
1849 while ((cmd = skipbl()) != '\n') {
1850 switch( cmd ){
1851 case 'b': size = 1; break;
1852 case 'w': size = 2; break;
1853 case 'l': size = 4; break;
1854 case 'd': size = 8; break;
1855 case 'r': brev = !brev; break;
1856 case 'n': mnoread = 1; break;
1857 case '.': mnoread = 0; break;
1858 }
1859 }
1860 if( size <= 0 )
1861 size = 1;
1862 else if( size > 8 )
1863 size = 8;
1864 for(;;){
1865 if (!mnoread)
1866 n = mread(adrs, val, size);
Paul Mackerrase1449ed2005-11-10 14:30:20 +11001867 printf(REG"%c", adrs, brev? 'r': ' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 if (!mnoread) {
1869 if (brev)
1870 byterev(val, size);
1871 putchar(' ');
1872 for (i = 0; i < n; ++i)
1873 printf("%.2x", val[i]);
1874 for (; i < size; ++i)
1875 printf("%s", fault_chars[fault_type]);
1876 }
1877 putchar(' ');
1878 inc = size;
1879 nslash = 0;
1880 for(;;){
1881 if( scanhex(&n) ){
1882 for (i = 0; i < size; ++i)
1883 val[i] = n >> (i * 8);
1884 if (!brev)
1885 byterev(val, size);
1886 mwrite(adrs, val, size);
1887 inc = size;
1888 }
1889 cmd = skipbl();
1890 if (cmd == '\n')
1891 break;
1892 inc = 0;
1893 switch (cmd) {
1894 case '\'':
1895 for(;;){
1896 n = inchar();
1897 if( n == '\\' )
1898 n = bsesc();
1899 else if( n == '\'' )
1900 break;
1901 for (i = 0; i < size; ++i)
1902 val[i] = n >> (i * 8);
1903 if (!brev)
1904 byterev(val, size);
1905 mwrite(adrs, val, size);
1906 adrs += size;
1907 }
1908 adrs -= size;
1909 inc = size;
1910 break;
1911 case ',':
1912 adrs += size;
1913 break;
1914 case '.':
1915 mnoread = 0;
1916 break;
1917 case ';':
1918 break;
1919 case 'x':
1920 case EOF:
1921 scannl();
1922 return;
1923 case 'b':
1924 case 'v':
1925 size = 1;
1926 break;
1927 case 'w':
1928 size = 2;
1929 break;
1930 case 'l':
1931 size = 4;
1932 break;
1933 case 'u':
1934 size = 8;
1935 break;
1936 case '^':
1937 adrs -= size;
1938 break;
1939 break;
1940 case '/':
1941 if (nslash > 0)
1942 adrs -= 1 << nslash;
1943 else
1944 nslash = 0;
1945 nslash += 4;
1946 adrs += 1 << nslash;
1947 break;
1948 case '\\':
1949 if (nslash < 0)
1950 adrs += 1 << -nslash;
1951 else
1952 nslash = 0;
1953 nslash -= 4;
1954 adrs -= 1 << -nslash;
1955 break;
1956 case 'm':
1957 scanhex((void *)&adrs);
1958 break;
1959 case 'n':
1960 mnoread = 1;
1961 break;
1962 case 'r':
1963 brev = !brev;
1964 break;
1965 case '<':
1966 n = size;
1967 scanhex(&n);
1968 adrs -= n;
1969 break;
1970 case '>':
1971 n = size;
1972 scanhex(&n);
1973 adrs += n;
1974 break;
1975 case '?':
1976 printf(memex_subcmd_help_string);
1977 break;
1978 }
1979 }
1980 adrs += inc;
1981 }
1982}
1983
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001984static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985bsesc(void)
1986{
1987 int c;
1988
1989 c = inchar();
1990 switch( c ){
1991 case 'n': c = '\n'; break;
1992 case 'r': c = '\r'; break;
1993 case 'b': c = '\b'; break;
1994 case 't': c = '\t'; break;
1995 }
1996 return c;
1997}
1998
Olaf Hering7e5b5932006-03-08 20:40:28 +01001999static void xmon_rawdump (unsigned long adrs, long ndump)
2000{
2001 long n, m, r, nr;
2002 unsigned char temp[16];
2003
2004 for (n = ndump; n > 0;) {
2005 r = n < 16? n: 16;
2006 nr = mread(adrs, temp, r);
2007 adrs += nr;
2008 for (m = 0; m < r; ++m) {
2009 if (m < nr)
2010 printf("%.2x", temp[m]);
2011 else
2012 printf("%s", fault_chars[fault_type]);
2013 }
2014 n -= r;
2015 if (nr < r)
2016 break;
2017 }
2018 printf("\n");
2019}
2020
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021#define isxdigit(c) (('0' <= (c) && (c) <= '9') \
2022 || ('a' <= (c) && (c) <= 'f') \
2023 || ('A' <= (c) && (c) <= 'F'))
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002024static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025dump(void)
2026{
2027 int c;
2028
2029 c = inchar();
2030 if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n')
2031 termch = c;
2032 scanhex((void *)&adrs);
2033 if (termch != '\n')
2034 termch = 0;
2035 if (c == 'i') {
2036 scanhex(&nidump);
2037 if (nidump == 0)
2038 nidump = 16;
2039 else if (nidump > MAX_DUMP)
2040 nidump = MAX_DUMP;
2041 adrs += ppc_inst_dump(adrs, nidump, 1);
2042 last_cmd = "di\n";
Vinay Sridharf312deb2009-05-14 23:13:07 +00002043 } else if (c == 'l') {
2044 dump_log_buf();
Olaf Hering7e5b5932006-03-08 20:40:28 +01002045 } else if (c == 'r') {
2046 scanhex(&ndump);
2047 if (ndump == 0)
2048 ndump = 64;
2049 xmon_rawdump(adrs, ndump);
2050 adrs += ndump;
2051 last_cmd = "dr\n";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 } else {
2053 scanhex(&ndump);
2054 if (ndump == 0)
2055 ndump = 64;
2056 else if (ndump > MAX_DUMP)
2057 ndump = MAX_DUMP;
2058 prdump(adrs, ndump);
2059 adrs += ndump;
2060 last_cmd = "d\n";
2061 }
2062}
2063
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002064static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065prdump(unsigned long adrs, long ndump)
2066{
2067 long n, m, c, r, nr;
2068 unsigned char temp[16];
2069
2070 for (n = ndump; n > 0;) {
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002071 printf(REG, adrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 putchar(' ');
2073 r = n < 16? n: 16;
2074 nr = mread(adrs, temp, r);
2075 adrs += nr;
2076 for (m = 0; m < r; ++m) {
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002077 if ((m & (sizeof(long) - 1)) == 0 && m > 0)
2078 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 if (m < nr)
2080 printf("%.2x", temp[m]);
2081 else
2082 printf("%s", fault_chars[fault_type]);
2083 }
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002084 for (; m < 16; ++m) {
2085 if ((m & (sizeof(long) - 1)) == 0)
2086 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 printf(" ");
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002088 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 printf(" |");
2090 for (m = 0; m < r; ++m) {
2091 if (m < nr) {
2092 c = temp[m];
2093 putchar(' ' <= c && c <= '~'? c: '.');
2094 } else
2095 putchar(' ');
2096 }
2097 n -= r;
2098 for (; m < 16; ++m)
2099 putchar(' ');
2100 printf("|\n");
2101 if (nr < r)
2102 break;
2103 }
2104}
2105
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002106typedef int (*instruction_dump_func)(unsigned long inst, unsigned long addr);
2107
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002108static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002109generic_inst_dump(unsigned long adr, long count, int praddr,
2110 instruction_dump_func dump_func)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111{
2112 int nr, dotted;
2113 unsigned long first_adr;
2114 unsigned long inst, last_inst = 0;
2115 unsigned char val[4];
2116
2117 dotted = 0;
2118 for (first_adr = adr; count > 0; --count, adr += 4) {
2119 nr = mread(adr, val, 4);
2120 if (nr == 0) {
2121 if (praddr) {
2122 const char *x = fault_chars[fault_type];
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002123 printf(REG" %s%s%s%s\n", adr, x, x, x, x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 }
2125 break;
2126 }
2127 inst = GETWORD(val);
2128 if (adr > first_adr && inst == last_inst) {
2129 if (!dotted) {
2130 printf(" ...\n");
2131 dotted = 1;
2132 }
2133 continue;
2134 }
2135 dotted = 0;
2136 last_inst = inst;
2137 if (praddr)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002138 printf(REG" %.8x", adr, inst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139 printf("\t");
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002140 dump_func(inst, adr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 printf("\n");
2142 }
2143 return adr - first_adr;
2144}
2145
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002146static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002147ppc_inst_dump(unsigned long adr, long count, int praddr)
2148{
2149 return generic_inst_dump(adr, count, praddr, print_insn_powerpc);
2150}
2151
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152void
2153print_address(unsigned long addr)
2154{
2155 xmon_print_symbol(addr, "\t# ", "");
2156}
2157
Vinay Sridharf312deb2009-05-14 23:13:07 +00002158void
2159dump_log_buf(void)
2160{
2161 const unsigned long size = 128;
Stephen Rothwell6d1386d2009-06-02 18:15:33 +00002162 unsigned long end, addr;
Vinay Sridharf312deb2009-05-14 23:13:07 +00002163 unsigned char buf[size + 1];
2164
2165 addr = 0;
2166 buf[size] = '\0';
2167
2168 if (setjmp(bus_error_jmp) != 0) {
2169 printf("Unable to lookup symbol __log_buf!\n");
2170 return;
2171 }
2172
2173 catch_memory_errors = 1;
2174 sync();
2175 addr = kallsyms_lookup_name("__log_buf");
2176
2177 if (! addr)
2178 printf("Symbol __log_buf not found!\n");
2179 else {
2180 end = addr + (1 << CONFIG_LOG_BUF_SHIFT);
2181 while (addr < end) {
2182 if (! mread(addr, buf, size)) {
2183 printf("Can't read memory at address 0x%lx\n", addr);
2184 break;
2185 }
2186
2187 printf("%s", buf);
2188
2189 if (strlen(buf) < size)
2190 break;
2191
2192 addr += size;
2193 }
2194 }
2195
2196 sync();
2197 /* wait a little while to see if we get a machine check */
2198 __delay(200);
2199 catch_memory_errors = 0;
2200}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201
2202/*
2203 * Memory operations - move, set, print differences
2204 */
2205static unsigned long mdest; /* destination address */
2206static unsigned long msrc; /* source address */
2207static unsigned long mval; /* byte value to set memory to */
2208static unsigned long mcount; /* # bytes to affect */
2209static unsigned long mdiffs; /* max # differences to print */
2210
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002211static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212memops(int cmd)
2213{
2214 scanhex((void *)&mdest);
2215 if( termch != '\n' )
2216 termch = 0;
2217 scanhex((void *)(cmd == 's'? &mval: &msrc));
2218 if( termch != '\n' )
2219 termch = 0;
2220 scanhex((void *)&mcount);
2221 switch( cmd ){
2222 case 'm':
2223 memmove((void *)mdest, (void *)msrc, mcount);
2224 break;
2225 case 's':
2226 memset((void *)mdest, mval, mcount);
2227 break;
2228 case 'd':
2229 if( termch != '\n' )
2230 termch = 0;
2231 scanhex((void *)&mdiffs);
2232 memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs);
2233 break;
2234 }
2235}
2236
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002237static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr)
2239{
2240 unsigned n, prt;
2241
2242 prt = 0;
2243 for( n = nb; n > 0; --n )
2244 if( *p1++ != *p2++ )
2245 if( ++prt <= maxpr )
2246 printf("%.16x %.2x # %.16x %.2x\n", p1 - 1,
2247 p1[-1], p2 - 1, p2[-1]);
2248 if( prt > maxpr )
2249 printf("Total of %d differences\n", prt);
2250}
2251
2252static unsigned mend;
2253static unsigned mask;
2254
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002255static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256memlocate(void)
2257{
2258 unsigned a, n;
2259 unsigned char val[4];
2260
2261 last_cmd = "ml";
2262 scanhex((void *)&mdest);
2263 if (termch != '\n') {
2264 termch = 0;
2265 scanhex((void *)&mend);
2266 if (termch != '\n') {
2267 termch = 0;
2268 scanhex((void *)&mval);
2269 mask = ~0;
2270 if (termch != '\n') termch = 0;
2271 scanhex((void *)&mask);
2272 }
2273 }
2274 n = 0;
2275 for (a = mdest; a < mend; a += 4) {
2276 if (mread(a, val, 4) == 4
2277 && ((GETWORD(val) ^ mval) & mask) == 0) {
2278 printf("%.16x: %.16x\n", a, GETWORD(val));
2279 if (++n >= 10)
2280 break;
2281 }
2282 }
2283}
2284
2285static unsigned long mskip = 0x1000;
2286static unsigned long mlim = 0xffffffff;
2287
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002288static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289memzcan(void)
2290{
2291 unsigned char v;
2292 unsigned a;
2293 int ok, ook;
2294
2295 scanhex(&mdest);
2296 if (termch != '\n') termch = 0;
2297 scanhex(&mskip);
2298 if (termch != '\n') termch = 0;
2299 scanhex(&mlim);
2300 ook = 0;
2301 for (a = mdest; a < mlim; a += mskip) {
2302 ok = mread(a, &v, 1);
2303 if (ok && !ook) {
2304 printf("%.8x .. ", a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305 } else if (!ok && ook)
2306 printf("%.8x\n", a - mskip);
2307 ook = ok;
2308 if (a + mskip < a)
2309 break;
2310 }
2311 if (ook)
2312 printf("%.8x\n", a - mskip);
2313}
2314
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002315static void proccall(void)
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002316{
2317 unsigned long args[8];
2318 unsigned long ret;
2319 int i;
2320 typedef unsigned long (*callfunc_t)(unsigned long, unsigned long,
2321 unsigned long, unsigned long, unsigned long,
2322 unsigned long, unsigned long, unsigned long);
2323 callfunc_t func;
2324
2325 if (!scanhex(&adrs))
2326 return;
2327 if (termch != '\n')
2328 termch = 0;
2329 for (i = 0; i < 8; ++i)
2330 args[i] = 0;
2331 for (i = 0; i < 8; ++i) {
2332 if (!scanhex(&args[i]) || termch == '\n')
2333 break;
2334 termch = 0;
2335 }
2336 func = (callfunc_t) adrs;
2337 ret = 0;
2338 if (setjmp(bus_error_jmp) == 0) {
2339 catch_memory_errors = 1;
2340 sync();
2341 ret = func(args[0], args[1], args[2], args[3],
2342 args[4], args[5], args[6], args[7]);
2343 sync();
2344 printf("return value is %x\n", ret);
2345 } else {
2346 printf("*** %x exception occurred\n", fault_except);
2347 }
2348 catch_memory_errors = 0;
2349}
2350
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351/* Input scanning routines */
2352int
2353skipbl(void)
2354{
2355 int c;
2356
2357 if( termch != 0 ){
2358 c = termch;
2359 termch = 0;
2360 } else
2361 c = inchar();
2362 while( c == ' ' || c == '\t' )
2363 c = inchar();
2364 return c;
2365}
2366
2367#define N_PTREGS 44
2368static char *regnames[N_PTREGS] = {
2369 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
2370 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
2371 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
2372 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002373 "pc", "msr", "or3", "ctr", "lr", "xer", "ccr",
2374#ifdef CONFIG_PPC64
2375 "softe",
2376#else
2377 "mq",
2378#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 "trap", "dar", "dsisr", "res"
2380};
2381
2382int
2383scanhex(unsigned long *vp)
2384{
2385 int c, d;
2386 unsigned long v;
2387
2388 c = skipbl();
2389 if (c == '%') {
2390 /* parse register name */
2391 char regname[8];
2392 int i;
2393
2394 for (i = 0; i < sizeof(regname) - 1; ++i) {
2395 c = inchar();
2396 if (!isalnum(c)) {
2397 termch = c;
2398 break;
2399 }
2400 regname[i] = c;
2401 }
2402 regname[i] = 0;
2403 for (i = 0; i < N_PTREGS; ++i) {
2404 if (strcmp(regnames[i], regname) == 0) {
2405 if (xmon_regs == NULL) {
2406 printf("regs not available\n");
2407 return 0;
2408 }
2409 *vp = ((unsigned long *)xmon_regs)[i];
2410 return 1;
2411 }
2412 }
2413 printf("invalid register name '%%%s'\n", regname);
2414 return 0;
2415 }
2416
2417 /* skip leading "0x" if any */
2418
2419 if (c == '0') {
2420 c = inchar();
2421 if (c == 'x') {
2422 c = inchar();
2423 } else {
2424 d = hexdigit(c);
2425 if (d == EOF) {
2426 termch = c;
2427 *vp = 0;
2428 return 1;
2429 }
2430 }
2431 } else if (c == '$') {
2432 int i;
2433 for (i=0; i<63; i++) {
2434 c = inchar();
2435 if (isspace(c)) {
2436 termch = c;
2437 break;
2438 }
2439 tmpstr[i] = c;
2440 }
2441 tmpstr[i++] = 0;
Benjamin Herrenschmidt6879dc12005-06-21 17:15:30 -07002442 *vp = 0;
2443 if (setjmp(bus_error_jmp) == 0) {
2444 catch_memory_errors = 1;
2445 sync();
2446 *vp = kallsyms_lookup_name(tmpstr);
2447 sync();
2448 }
2449 catch_memory_errors = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450 if (!(*vp)) {
2451 printf("unknown symbol '%s'\n", tmpstr);
2452 return 0;
2453 }
2454 return 1;
2455 }
2456
2457 d = hexdigit(c);
2458 if (d == EOF) {
2459 termch = c;
2460 return 0;
2461 }
2462 v = 0;
2463 do {
2464 v = (v << 4) + d;
2465 c = inchar();
2466 d = hexdigit(c);
2467 } while (d != EOF);
2468 termch = c;
2469 *vp = v;
2470 return 1;
2471}
2472
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002473static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474scannl(void)
2475{
2476 int c;
2477
2478 c = termch;
2479 termch = 0;
2480 while( c != '\n' )
2481 c = inchar();
2482}
2483
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002484static int hexdigit(int c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485{
2486 if( '0' <= c && c <= '9' )
2487 return c - '0';
2488 if( 'A' <= c && c <= 'F' )
2489 return c - ('A' - 10);
2490 if( 'a' <= c && c <= 'f' )
2491 return c - ('a' - 10);
2492 return EOF;
2493}
2494
2495void
2496getstring(char *s, int size)
2497{
2498 int c;
2499
2500 c = skipbl();
2501 do {
2502 if( size > 1 ){
2503 *s++ = c;
2504 --size;
2505 }
2506 c = inchar();
2507 } while( c != ' ' && c != '\t' && c != '\n' );
2508 termch = c;
2509 *s = 0;
2510}
2511
2512static char line[256];
2513static char *lineptr;
2514
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002515static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516flush_input(void)
2517{
2518 lineptr = NULL;
2519}
2520
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002521static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522inchar(void)
2523{
2524 if (lineptr == NULL || *lineptr == 0) {
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002525 if (xmon_gets(line, sizeof(line)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 lineptr = NULL;
2527 return EOF;
2528 }
2529 lineptr = line;
2530 }
2531 return *lineptr++;
2532}
2533
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002534static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535take_input(char *str)
2536{
2537 lineptr = str;
2538}
2539
2540
2541static void
2542symbol_lookup(void)
2543{
2544 int type = inchar();
2545 unsigned long addr;
2546 static char tmp[64];
2547
2548 switch (type) {
2549 case 'a':
2550 if (scanhex(&addr))
2551 xmon_print_symbol(addr, ": ", "\n");
2552 termch = 0;
2553 break;
2554 case 's':
2555 getstring(tmp, 64);
2556 if (setjmp(bus_error_jmp) == 0) {
2557 catch_memory_errors = 1;
2558 sync();
2559 addr = kallsyms_lookup_name(tmp);
2560 if (addr)
2561 printf("%s: %lx\n", tmp, addr);
2562 else
2563 printf("Symbol '%s' not found.\n", tmp);
2564 sync();
2565 }
2566 catch_memory_errors = 0;
2567 termch = 0;
2568 break;
2569 }
2570}
2571
2572
2573/* Print an address in numeric and symbolic form (if possible) */
2574static void xmon_print_symbol(unsigned long address, const char *mid,
2575 const char *after)
2576{
2577 char *modname;
2578 const char *name = NULL;
2579 unsigned long offset, size;
2580
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002581 printf(REG, address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582 if (setjmp(bus_error_jmp) == 0) {
2583 catch_memory_errors = 1;
2584 sync();
2585 name = kallsyms_lookup(address, &size, &offset, &modname,
2586 tmpstr);
2587 sync();
2588 /* wait a little while to see if we get a machine check */
2589 __delay(200);
2590 }
2591
2592 catch_memory_errors = 0;
2593
2594 if (name) {
2595 printf("%s%s+%#lx/%#lx", mid, name, offset, size);
2596 if (modname)
2597 printf(" [%s]", modname);
2598 }
2599 printf("%s", after);
2600}
2601
Benjamin Herrenschmidt2d27cfd2009-07-23 23:15:59 +00002602#ifdef CONFIG_PPC_BOOK3S_64
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603static void dump_slb(void)
2604{
2605 int i;
will schmidtb3b95952007-12-07 08:22:23 +11002606 unsigned long esid,vsid,valid;
2607 unsigned long llp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608
2609 printf("SLB contents of cpu %x\n", smp_processor_id());
2610
Michael Neuling584f8b72007-12-06 17:24:48 +11002611 for (i = 0; i < mmu_slb_size; i++) {
will schmidtb3b95952007-12-07 08:22:23 +11002612 asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i));
2613 asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i));
2614 valid = (esid & SLB_ESID_V);
2615 if (valid | esid | vsid) {
2616 printf("%02d %016lx %016lx", i, esid, vsid);
2617 if (valid) {
2618 llp = vsid & SLB_VSID_LLP;
2619 if (vsid & SLB_VSID_B_1T) {
2620 printf(" 1T ESID=%9lx VSID=%13lx LLP:%3lx \n",
2621 GET_ESID_1T(esid),
2622 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT_1T,
2623 llp);
2624 } else {
2625 printf(" 256M ESID=%9lx VSID=%13lx LLP:%3lx \n",
2626 GET_ESID(esid),
2627 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT,
2628 llp);
2629 }
2630 } else
2631 printf("\n");
2632 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633 }
2634}
2635
2636static void dump_stab(void)
2637{
2638 int i;
2639 unsigned long *tmp = (unsigned long *)get_paca()->stab_addr;
2640
2641 printf("Segment table contents of cpu %x\n", smp_processor_id());
2642
2643 for (i = 0; i < PAGE_SIZE/16; i++) {
2644 unsigned long a, b;
2645
2646 a = *tmp++;
2647 b = *tmp++;
2648
2649 if (a || b) {
2650 printf("%03d %016lx ", i, a);
2651 printf("%016lx\n", b);
2652 }
2653 }
2654}
2655
Paul Mackerrasf78541d2005-10-28 22:53:37 +10002656void dump_segments(void)
2657{
2658 if (cpu_has_feature(CPU_FTR_SLB))
2659 dump_slb();
2660 else
2661 dump_stab();
2662}
2663#endif
2664
2665#ifdef CONFIG_PPC_STD_MMU_32
2666void dump_segments(void)
2667{
2668 int i;
2669
2670 printf("sr0-15 =");
2671 for (i = 0; i < 16; ++i)
2672 printf(" %x", mfsrin(i));
2673 printf("\n");
2674}
2675#endif
2676
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +11002677#ifdef CONFIG_44x
2678static void dump_tlb_44x(void)
2679{
2680 int i;
2681
2682 for (i = 0; i < PPC44x_TLB_SIZE; i++) {
2683 unsigned long w0,w1,w2;
2684 asm volatile("tlbre %0,%1,0" : "=r" (w0) : "r" (i));
2685 asm volatile("tlbre %0,%1,1" : "=r" (w1) : "r" (i));
2686 asm volatile("tlbre %0,%1,2" : "=r" (w2) : "r" (i));
2687 printf("[%02x] %08x %08x %08x ", i, w0, w1, w2);
2688 if (w0 & PPC44x_TLB_VALID) {
2689 printf("V %08x -> %01x%08x %c%c%c%c%c",
2690 w0 & PPC44x_TLB_EPN_MASK,
2691 w1 & PPC44x_TLB_ERPN_MASK,
2692 w1 & PPC44x_TLB_RPN_MASK,
2693 (w2 & PPC44x_TLB_W) ? 'W' : 'w',
2694 (w2 & PPC44x_TLB_I) ? 'I' : 'i',
2695 (w2 & PPC44x_TLB_M) ? 'M' : 'm',
2696 (w2 & PPC44x_TLB_G) ? 'G' : 'g',
2697 (w2 & PPC44x_TLB_E) ? 'E' : 'e');
2698 }
2699 printf("\n");
2700 }
2701}
2702#endif /* CONFIG_44x */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002703
2704static void xmon_init(int enable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705{
Stephen Rothwellbbb68172006-11-30 11:44:09 +11002706#ifdef CONFIG_PPC_ISERIES
2707 if (firmware_has_feature(FW_FEATURE_ISERIES))
2708 return;
2709#endif
Olaf Heringb13cfd172005-08-04 19:26:42 +02002710 if (enable) {
2711 __debugger = xmon;
2712 __debugger_ipi = xmon_ipi;
2713 __debugger_bpt = xmon_bpt;
2714 __debugger_sstep = xmon_sstep;
2715 __debugger_iabr_match = xmon_iabr_match;
2716 __debugger_dabr_match = xmon_dabr_match;
2717 __debugger_fault_handler = xmon_fault_handler;
2718 } else {
2719 __debugger = NULL;
2720 __debugger_ipi = NULL;
2721 __debugger_bpt = NULL;
2722 __debugger_sstep = NULL;
2723 __debugger_iabr_match = NULL;
2724 __debugger_dabr_match = NULL;
2725 __debugger_fault_handler = NULL;
2726 }
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002727 xmon_map_scc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728}
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002729
2730#ifdef CONFIG_MAGIC_SYSRQ
David Howells7d12e782006-10-05 14:55:46 +01002731static void sysrq_handle_xmon(int key, struct tty_struct *tty)
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002732{
2733 /* ensure xmon is enabled */
2734 xmon_init(1);
David Howells7d12e782006-10-05 14:55:46 +01002735 debugger(get_irq_regs());
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002736}
2737
2738static struct sysrq_key_op sysrq_xmon_op =
2739{
2740 .handler = sysrq_handle_xmon,
2741 .help_msg = "Xmon",
2742 .action_msg = "Entering xmon",
2743};
2744
2745static int __init setup_xmon_sysrq(void)
2746{
Stephen Rothwellbbb68172006-11-30 11:44:09 +11002747#ifdef CONFIG_PPC_ISERIES
2748 if (firmware_has_feature(FW_FEATURE_ISERIES))
2749 return 0;
2750#endif
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002751 register_sysrq_key('x', &sysrq_xmon_op);
2752 return 0;
2753}
2754__initcall(setup_xmon_sysrq);
2755#endif /* CONFIG_MAGIC_SYSRQ */
Michael Ellerman476792832006-10-03 14:12:08 +10002756
Olaf Heringf5e6a282007-06-24 16:57:08 +10002757static int __initdata xmon_early, xmon_off;
Michael Ellerman476792832006-10-03 14:12:08 +10002758
2759static int __init early_parse_xmon(char *p)
2760{
2761 if (!p || strncmp(p, "early", 5) == 0) {
2762 /* just "xmon" is equivalent to "xmon=early" */
2763 xmon_init(1);
2764 xmon_early = 1;
2765 } else if (strncmp(p, "on", 2) == 0)
2766 xmon_init(1);
2767 else if (strncmp(p, "off", 3) == 0)
2768 xmon_off = 1;
2769 else if (strncmp(p, "nobt", 4) == 0)
2770 xmon_no_auto_backtrace = 1;
2771 else
2772 return 1;
2773
2774 return 0;
2775}
2776early_param("xmon", early_parse_xmon);
2777
2778void __init xmon_setup(void)
2779{
2780#ifdef CONFIG_XMON_DEFAULT
2781 if (!xmon_off)
2782 xmon_init(1);
2783#endif
2784 if (xmon_early)
2785 debugger(NULL);
2786}
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002787
Arnd Bergmanne0555952006-11-27 19:18:55 +01002788#ifdef CONFIG_SPU_BASE
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002789
2790struct spu_info {
2791 struct spu *spu;
2792 u64 saved_mfc_sr1_RW;
2793 u32 saved_spu_runcntl_RW;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002794 unsigned long dump_addr;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002795 u8 stopped_ok;
2796};
2797
2798#define XMON_NUM_SPUS 16 /* Enough for current hardware */
2799
2800static struct spu_info spu_info[XMON_NUM_SPUS];
2801
2802void xmon_register_spus(struct list_head *list)
2803{
2804 struct spu *spu;
2805
2806 list_for_each_entry(spu, list, full_list) {
2807 if (spu->number >= XMON_NUM_SPUS) {
2808 WARN_ON(1);
2809 continue;
2810 }
2811
2812 spu_info[spu->number].spu = spu;
2813 spu_info[spu->number].stopped_ok = 0;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002814 spu_info[spu->number].dump_addr = (unsigned long)
2815 spu_info[spu->number].spu->local_store;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002816 }
2817}
2818
2819static void stop_spus(void)
2820{
2821 struct spu *spu;
2822 int i;
2823 u64 tmp;
2824
2825 for (i = 0; i < XMON_NUM_SPUS; i++) {
2826 if (!spu_info[i].spu)
2827 continue;
2828
2829 if (setjmp(bus_error_jmp) == 0) {
2830 catch_memory_errors = 1;
2831 sync();
2832
2833 spu = spu_info[i].spu;
2834
2835 spu_info[i].saved_spu_runcntl_RW =
2836 in_be32(&spu->problem->spu_runcntl_RW);
2837
2838 tmp = spu_mfc_sr1_get(spu);
2839 spu_info[i].saved_mfc_sr1_RW = tmp;
2840
2841 tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
2842 spu_mfc_sr1_set(spu, tmp);
2843
2844 sync();
2845 __delay(200);
2846
2847 spu_info[i].stopped_ok = 1;
Michael Ellerman2a144422006-11-23 00:46:40 +01002848
2849 printf("Stopped spu %.2d (was %s)\n", i,
2850 spu_info[i].saved_spu_runcntl_RW ?
2851 "running" : "stopped");
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002852 } else {
2853 catch_memory_errors = 0;
2854 printf("*** Error stopping spu %.2d\n", i);
2855 }
2856 catch_memory_errors = 0;
2857 }
2858}
2859
2860static void restart_spus(void)
2861{
2862 struct spu *spu;
2863 int i;
2864
2865 for (i = 0; i < XMON_NUM_SPUS; i++) {
2866 if (!spu_info[i].spu)
2867 continue;
2868
2869 if (!spu_info[i].stopped_ok) {
2870 printf("*** Error, spu %d was not successfully stopped"
2871 ", not restarting\n", i);
2872 continue;
2873 }
2874
2875 if (setjmp(bus_error_jmp) == 0) {
2876 catch_memory_errors = 1;
2877 sync();
2878
2879 spu = spu_info[i].spu;
2880 spu_mfc_sr1_set(spu, spu_info[i].saved_mfc_sr1_RW);
2881 out_be32(&spu->problem->spu_runcntl_RW,
2882 spu_info[i].saved_spu_runcntl_RW);
2883
2884 sync();
2885 __delay(200);
2886
2887 printf("Restarted spu %.2d\n", i);
2888 } else {
2889 catch_memory_errors = 0;
2890 printf("*** Error restarting spu %.2d\n", i);
2891 }
2892 catch_memory_errors = 0;
2893 }
2894}
2895
Michael Ellermana8984972006-10-24 18:31:28 +02002896#define DUMP_WIDTH 23
Michael Ellerman437a0702006-11-23 00:46:39 +01002897#define DUMP_VALUE(format, field, value) \
Michael Ellermana8984972006-10-24 18:31:28 +02002898do { \
2899 if (setjmp(bus_error_jmp) == 0) { \
2900 catch_memory_errors = 1; \
2901 sync(); \
2902 printf(" %-*s = "format"\n", DUMP_WIDTH, \
Michael Ellerman437a0702006-11-23 00:46:39 +01002903 #field, value); \
Michael Ellermana8984972006-10-24 18:31:28 +02002904 sync(); \
2905 __delay(200); \
2906 } else { \
2907 catch_memory_errors = 0; \
2908 printf(" %-*s = *** Error reading field.\n", \
2909 DUMP_WIDTH, #field); \
2910 } \
2911 catch_memory_errors = 0; \
2912} while (0)
2913
Michael Ellerman437a0702006-11-23 00:46:39 +01002914#define DUMP_FIELD(obj, format, field) \
2915 DUMP_VALUE(format, field, obj->field)
2916
Michael Ellermana8984972006-10-24 18:31:28 +02002917static void dump_spu_fields(struct spu *spu)
2918{
2919 printf("Dumping spu fields at address %p:\n", spu);
2920
2921 DUMP_FIELD(spu, "0x%x", number);
2922 DUMP_FIELD(spu, "%s", name);
Michael Ellermana8984972006-10-24 18:31:28 +02002923 DUMP_FIELD(spu, "0x%lx", local_store_phys);
2924 DUMP_FIELD(spu, "0x%p", local_store);
2925 DUMP_FIELD(spu, "0x%lx", ls_size);
2926 DUMP_FIELD(spu, "0x%x", node);
2927 DUMP_FIELD(spu, "0x%lx", flags);
Michael Ellermana8984972006-10-24 18:31:28 +02002928 DUMP_FIELD(spu, "%d", class_0_pending);
Luke Browningf3d69e02008-04-27 18:41:55 +00002929 DUMP_FIELD(spu, "0x%lx", class_0_dar);
Luke Browningf3d69e02008-04-27 18:41:55 +00002930 DUMP_FIELD(spu, "0x%lx", class_1_dar);
2931 DUMP_FIELD(spu, "0x%lx", class_1_dsisr);
Michael Ellermana8984972006-10-24 18:31:28 +02002932 DUMP_FIELD(spu, "0x%lx", irqs[0]);
2933 DUMP_FIELD(spu, "0x%lx", irqs[1]);
2934 DUMP_FIELD(spu, "0x%lx", irqs[2]);
2935 DUMP_FIELD(spu, "0x%x", slb_replace);
2936 DUMP_FIELD(spu, "%d", pid);
Michael Ellermana8984972006-10-24 18:31:28 +02002937 DUMP_FIELD(spu, "0x%p", mm);
2938 DUMP_FIELD(spu, "0x%p", ctx);
2939 DUMP_FIELD(spu, "0x%p", rq);
2940 DUMP_FIELD(spu, "0x%p", timestamp);
2941 DUMP_FIELD(spu, "0x%lx", problem_phys);
2942 DUMP_FIELD(spu, "0x%p", problem);
Michael Ellerman437a0702006-11-23 00:46:39 +01002943 DUMP_VALUE("0x%x", problem->spu_runcntl_RW,
2944 in_be32(&spu->problem->spu_runcntl_RW));
2945 DUMP_VALUE("0x%x", problem->spu_status_R,
2946 in_be32(&spu->problem->spu_status_R));
2947 DUMP_VALUE("0x%x", problem->spu_npc_RW,
2948 in_be32(&spu->problem->spu_npc_RW));
Michael Ellermana8984972006-10-24 18:31:28 +02002949 DUMP_FIELD(spu, "0x%p", priv2);
Michael Ellermana9852392006-11-23 00:46:50 +01002950 DUMP_FIELD(spu, "0x%p", pdata);
Michael Ellermana8984972006-10-24 18:31:28 +02002951}
2952
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002953int
2954spu_inst_dump(unsigned long adr, long count, int praddr)
2955{
2956 return generic_inst_dump(adr, count, praddr, print_insn_spu);
2957}
2958
2959static void dump_spu_ls(unsigned long num, int subcmd)
Michael Ellerman24a24c82006-11-23 00:46:41 +01002960{
2961 unsigned long offset, addr, ls_addr;
2962
2963 if (setjmp(bus_error_jmp) == 0) {
2964 catch_memory_errors = 1;
2965 sync();
2966 ls_addr = (unsigned long)spu_info[num].spu->local_store;
2967 sync();
2968 __delay(200);
2969 } else {
2970 catch_memory_errors = 0;
2971 printf("*** Error: accessing spu info for spu %d\n", num);
2972 return;
2973 }
2974 catch_memory_errors = 0;
2975
2976 if (scanhex(&offset))
2977 addr = ls_addr + offset;
2978 else
2979 addr = spu_info[num].dump_addr;
2980
2981 if (addr >= ls_addr + LS_SIZE) {
2982 printf("*** Error: address outside of local store\n");
2983 return;
2984 }
2985
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002986 switch (subcmd) {
2987 case 'i':
2988 addr += spu_inst_dump(addr, 16, 1);
2989 last_cmd = "sdi\n";
2990 break;
2991 default:
2992 prdump(addr, 64);
2993 addr += 64;
2994 last_cmd = "sd\n";
2995 break;
2996 }
Michael Ellerman24a24c82006-11-23 00:46:41 +01002997
2998 spu_info[num].dump_addr = addr;
2999}
3000
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003001static int do_spu_cmd(void)
3002{
Michael Ellerman24a24c82006-11-23 00:46:41 +01003003 static unsigned long num = 0;
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003004 int cmd, subcmd = 0;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003005
3006 cmd = inchar();
3007 switch (cmd) {
3008 case 's':
3009 stop_spus();
3010 break;
3011 case 'r':
3012 restart_spus();
3013 break;
Michael Ellerman24a24c82006-11-23 00:46:41 +01003014 case 'd':
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003015 subcmd = inchar();
3016 if (isxdigit(subcmd) || subcmd == '\n')
3017 termch = subcmd;
3018 case 'f':
Michael Ellerman24a24c82006-11-23 00:46:41 +01003019 scanhex(&num);
3020 if (num >= XMON_NUM_SPUS || !spu_info[num].spu) {
Michael Ellermana8984972006-10-24 18:31:28 +02003021 printf("*** Error: invalid spu number\n");
Michael Ellerman24a24c82006-11-23 00:46:41 +01003022 return 0;
3023 }
3024
3025 switch (cmd) {
3026 case 'f':
3027 dump_spu_fields(spu_info[num].spu);
3028 break;
3029 default:
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003030 dump_spu_ls(num, subcmd);
Michael Ellerman24a24c82006-11-23 00:46:41 +01003031 break;
3032 }
3033
Michael Ellermana8984972006-10-24 18:31:28 +02003034 break;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003035 default:
3036 return -1;
3037 }
3038
3039 return 0;
3040}
Arnd Bergmanne0555952006-11-27 19:18:55 +01003041#else /* ! CONFIG_SPU_BASE */
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003042static int do_spu_cmd(void)
3043{
3044 return -1;
3045}
3046#endif