blob: c6f0a71b405ee97bce3f7753147cfe4adc1f3fda [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 Mackerrasf78541dc2005-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 Mackerrasf78541dc2005-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 Mackerrasf78541dc2005-10-28 22:53:37 +100045
46#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#include <asm/hvcall.h>
Paul Mackerrasf78541dc2005-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
64static unsigned long in_xmon = 0;
65
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 Mackerrasf78541dc2005-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 Mackerrasf78541dc2005-10-28 22:53:37 +1000161extern void xmon_enter(void);
162extern void xmon_leave(void);
163
Paul Mackerrasf78541dc2005-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 Mackerrasf78541dc2005-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 Bergmanne055595d2006-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 Mackerrasf78541dc2005-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 Mackerrasf78541dc2005-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 Mackerrasf78541dc2005-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 Mackerrasf78541dc2005-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 Mackerrasf78541dc2005-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 Mackerrasf78541dc2005-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
520 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
521 bp = at_breakpoint(regs->nip);
522 if (bp != NULL) {
523 int stepped = emulate_step(regs, bp->instr[0]);
524 if (stepped == 0) {
525 regs->nip = (unsigned long) &bp->instr[0];
526 atomic_inc(&bp->ref_count);
527 } else if (stepped < 0) {
528 printf("Couldn't single-step %s instruction\n",
529 (IS_RFID(bp->instr[0])? "rfid": "mtmsrd"));
530 }
531 }
532 }
533
534 insert_cpu_bpts();
535
Anton Blanchardf13659e2007-03-21 01:48:34 +1100536 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Paul Mackerras0a730ae2006-10-03 21:32:49 +1000538 return cmd != 'X' && cmd != EOF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539}
540
541int xmon(struct pt_regs *excp)
542{
543 struct pt_regs regs;
544
545 if (excp == NULL) {
Anton Vorontsov322b4392008-12-17 10:08:55 +0000546 ppc_save_regs(&regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 excp = &regs;
548 }
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200549
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 return xmon_core(excp, 0);
551}
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000552EXPORT_SYMBOL(xmon);
553
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000554irqreturn_t xmon_irq(int irq, void *d)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000555{
556 unsigned long flags;
557 local_irq_save(flags);
558 printf("Keyboard interrupt\n");
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000559 xmon(get_irq_regs());
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000560 local_irq_restore(flags);
561 return IRQ_HANDLED;
562}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000564static int xmon_bpt(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
566 struct bpt *bp;
567 unsigned long offset;
568
569 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
570 return 0;
571
572 /* Are we at the trap at bp->instr[1] for some bp? */
573 bp = in_breakpoint_table(regs->nip, &offset);
574 if (bp != NULL && offset == 4) {
575 regs->nip = bp->address + 4;
576 atomic_dec(&bp->ref_count);
577 return 1;
578 }
579
580 /* Are we at a breakpoint? */
581 bp = at_breakpoint(regs->nip);
582 if (!bp)
583 return 0;
584
585 xmon_core(regs, 0);
586
587 return 1;
588}
589
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000590static int xmon_sstep(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
592 if (user_mode(regs))
593 return 0;
594 xmon_core(regs, 0);
595 return 1;
596}
597
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000598static int xmon_dabr_match(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599{
600 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
601 return 0;
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000602 if (dabr.enabled == 0)
603 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 xmon_core(regs, 0);
605 return 1;
606}
607
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000608static int xmon_iabr_match(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
610 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
611 return 0;
Michael Ellerman9f1067c22008-05-08 14:27:16 +1000612 if (iabr == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 return 0;
614 xmon_core(regs, 0);
615 return 1;
616}
617
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000618static int xmon_ipi(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619{
620#ifdef CONFIG_SMP
621 if (in_xmon && !cpu_isset(smp_processor_id(), cpus_in_xmon))
622 xmon_core(regs, 1);
623#endif
624 return 0;
625}
626
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000627static int xmon_fault_handler(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628{
629 struct bpt *bp;
630 unsigned long offset;
631
632 if (in_xmon && catch_memory_errors)
633 handle_fault(regs); /* doesn't return */
634
635 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
636 bp = in_breakpoint_table(regs->nip, &offset);
637 if (bp != NULL) {
638 regs->nip = bp->address + offset;
639 atomic_dec(&bp->ref_count);
640 }
641 }
642
643 return 0;
644}
645
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646static struct bpt *at_breakpoint(unsigned long pc)
647{
648 int i;
649 struct bpt *bp;
650
651 bp = bpts;
652 for (i = 0; i < NBPTS; ++i, ++bp)
653 if (bp->enabled && pc == bp->address)
654 return bp;
655 return NULL;
656}
657
658static struct bpt *in_breakpoint_table(unsigned long nip, unsigned long *offp)
659{
660 unsigned long off;
661
662 off = nip - (unsigned long) bpts;
663 if (off >= sizeof(bpts))
664 return NULL;
665 off %= sizeof(struct bpt);
666 if (off != offsetof(struct bpt, instr[0])
667 && off != offsetof(struct bpt, instr[1]))
668 return NULL;
669 *offp = off - offsetof(struct bpt, instr[0]);
670 return (struct bpt *) (nip - off);
671}
672
673static struct bpt *new_breakpoint(unsigned long a)
674{
675 struct bpt *bp;
676
677 a &= ~3UL;
678 bp = at_breakpoint(a);
679 if (bp)
680 return bp;
681
682 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
683 if (!bp->enabled && atomic_read(&bp->ref_count) == 0) {
684 bp->address = a;
685 bp->instr[1] = bpinstr;
686 store_inst(&bp->instr[1]);
687 return bp;
688 }
689 }
690
691 printf("Sorry, no free breakpoints. Please clear one first.\n");
692 return NULL;
693}
694
695static void insert_bpts(void)
696{
697 int i;
698 struct bpt *bp;
699
700 bp = bpts;
701 for (i = 0; i < NBPTS; ++i, ++bp) {
702 if ((bp->enabled & (BP_TRAP|BP_IABR)) == 0)
703 continue;
704 if (mread(bp->address, &bp->instr[0], 4) != 4) {
705 printf("Couldn't read instruction at %lx, "
706 "disabling breakpoint there\n", bp->address);
707 bp->enabled = 0;
708 continue;
709 }
710 if (IS_MTMSRD(bp->instr[0]) || IS_RFID(bp->instr[0])) {
711 printf("Breakpoint at %lx is on an mtmsrd or rfid "
712 "instruction, disabling it\n", bp->address);
713 bp->enabled = 0;
714 continue;
715 }
716 store_inst(&bp->instr[0]);
717 if (bp->enabled & BP_IABR)
718 continue;
719 if (mwrite(bp->address, &bpinstr, 4) != 4) {
720 printf("Couldn't write instruction at %lx, "
721 "disabling breakpoint there\n", bp->address);
722 bp->enabled &= ~BP_TRAP;
723 continue;
724 }
725 store_inst((void *)bp->address);
726 }
727}
728
729static void insert_cpu_bpts(void)
730{
731 if (dabr.enabled)
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000732 set_dabr(dabr.address | (dabr.enabled & 7));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 if (iabr && cpu_has_feature(CPU_FTR_IABR))
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000734 mtspr(SPRN_IABR, iabr->address
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
736}
737
738static void remove_bpts(void)
739{
740 int i;
741 struct bpt *bp;
742 unsigned instr;
743
744 bp = bpts;
745 for (i = 0; i < NBPTS; ++i, ++bp) {
746 if ((bp->enabled & (BP_TRAP|BP_IABR)) != BP_TRAP)
747 continue;
748 if (mread(bp->address, &instr, 4) == 4
749 && instr == bpinstr
750 && mwrite(bp->address, &bp->instr, 4) != 4)
751 printf("Couldn't remove breakpoint at %lx\n",
752 bp->address);
753 else
754 store_inst((void *)bp->address);
755 }
756}
757
758static void remove_cpu_bpts(void)
759{
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000760 set_dabr(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 if (cpu_has_feature(CPU_FTR_IABR))
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000762 mtspr(SPRN_IABR, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763}
764
765/* Command interpreting routine */
766static char *last_cmd;
767
768static int
769cmds(struct pt_regs *excp)
770{
771 int cmd = 0;
772
773 last_cmd = NULL;
774 xmon_regs = excp;
Olaf Hering26c8af52006-09-08 16:29:21 +0200775
776 if (!xmon_no_auto_backtrace) {
777 xmon_no_auto_backtrace = 1;
778 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
779 }
780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 for(;;) {
782#ifdef CONFIG_SMP
783 printf("%x:", smp_processor_id());
784#endif /* CONFIG_SMP */
785 printf("mon> ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 flush_input();
787 termch = 0;
788 cmd = skipbl();
789 if( cmd == '\n' ) {
790 if (last_cmd == NULL)
791 continue;
792 take_input(last_cmd);
793 last_cmd = NULL;
794 cmd = inchar();
795 }
796 switch (cmd) {
797 case 'm':
798 cmd = inchar();
799 switch (cmd) {
800 case 'm':
801 case 's':
802 case 'd':
803 memops(cmd);
804 break;
805 case 'l':
806 memlocate();
807 break;
808 case 'z':
809 memzcan();
810 break;
811 case 'i':
812 show_mem();
813 break;
814 default:
815 termch = cmd;
816 memex();
817 }
818 break;
819 case 'd':
820 dump();
821 break;
822 case 'l':
823 symbol_lookup();
824 break;
825 case 'r':
826 prregs(excp); /* print regs */
827 break;
828 case 'e':
829 excprint(excp);
830 break;
831 case 'S':
832 super_regs();
833 break;
834 case 't':
835 backtrace(excp);
836 break;
837 case 'f':
838 cacheflush();
839 break;
840 case 's':
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200841 if (do_spu_cmd() == 0)
842 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 if (do_step(excp))
844 return cmd;
845 break;
846 case 'x':
847 case 'X':
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100848 return cmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 case EOF:
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100850 printf(" <no input ...>\n");
851 mdelay(2000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 return cmd;
853 case '?':
Ishizaki Kou4d404ed2007-07-18 19:26:40 +1000854 xmon_puts(help_string);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 case 'b':
857 bpt_cmds();
858 break;
859 case 'C':
860 csum();
861 break;
862 case 'c':
863 if (cpu_cmd())
864 return 0;
865 break;
866 case 'z':
867 bootcmds();
868 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000869 case 'p':
870 proccall();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000872#ifdef CONFIG_PPC_STD_MMU
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 case 'u':
874 dump_segments();
875 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000876#endif
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100877#ifdef CONFIG_4xx
878 case 'u':
879 dump_tlb_44x();
880 break;
881#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 default:
883 printf("Unrecognized command: ");
884 do {
885 if (' ' < cmd && cmd <= '~')
886 putchar(cmd);
887 else
888 printf("\\x%x", cmd);
889 cmd = inchar();
890 } while (cmd != '\n');
891 printf(" (type ? for help)\n");
892 break;
893 }
894 }
895}
896
897/*
898 * Step a single instruction.
899 * Some instructions we emulate, others we execute with MSR_SE set.
900 */
901static int do_step(struct pt_regs *regs)
902{
903 unsigned int instr;
904 int stepped;
905
906 /* check we are in 64-bit kernel mode, translation enabled */
907 if ((regs->msr & (MSR_SF|MSR_PR|MSR_IR)) == (MSR_SF|MSR_IR)) {
908 if (mread(regs->nip, &instr, 4) == 4) {
909 stepped = emulate_step(regs, instr);
910 if (stepped < 0) {
911 printf("Couldn't single-step %s instruction\n",
912 (IS_RFID(instr)? "rfid": "mtmsrd"));
913 return 0;
914 }
915 if (stepped > 0) {
916 regs->trap = 0xd00 | (regs->trap & 1);
917 printf("stepped to ");
918 xmon_print_symbol(regs->nip, " ", "\n");
919 ppc_inst_dump(regs->nip, 1, 0);
920 return 0;
921 }
922 }
923 }
924 regs->msr |= MSR_SE;
925 return 1;
926}
927
928static void bootcmds(void)
929{
930 int cmd;
931
932 cmd = inchar();
933 if (cmd == 'r')
934 ppc_md.restart(NULL);
935 else if (cmd == 'h')
936 ppc_md.halt();
937 else if (cmd == 'p')
938 ppc_md.power_off();
939}
940
941static int cpu_cmd(void)
942{
943#ifdef CONFIG_SMP
944 unsigned long cpu;
945 int timeout;
946 int count;
947
948 if (!scanhex(&cpu)) {
949 /* print cpus waiting or in xmon */
950 printf("cpus stopped:");
951 count = 0;
952 for (cpu = 0; cpu < NR_CPUS; ++cpu) {
953 if (cpu_isset(cpu, cpus_in_xmon)) {
954 if (count == 0)
955 printf(" %x", cpu);
956 ++count;
957 } else {
958 if (count > 1)
959 printf("-%x", cpu - 1);
960 count = 0;
961 }
962 }
963 if (count > 1)
964 printf("-%x", NR_CPUS - 1);
965 printf("\n");
966 return 0;
967 }
968 /* try to switch to cpu specified */
969 if (!cpu_isset(cpu, cpus_in_xmon)) {
970 printf("cpu 0x%x isn't in xmon\n", cpu);
971 return 0;
972 }
973 xmon_taken = 0;
974 mb();
975 xmon_owner = cpu;
976 timeout = 10000000;
977 while (!xmon_taken) {
978 if (--timeout == 0) {
979 if (test_and_set_bit(0, &xmon_taken))
980 break;
981 /* take control back */
982 mb();
983 xmon_owner = smp_processor_id();
984 printf("cpu %u didn't take control\n", cpu);
985 return 0;
986 }
987 barrier();
988 }
989 return 1;
990#else
991 return 0;
992#endif /* CONFIG_SMP */
993}
994
995static unsigned short fcstab[256] = {
996 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
997 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
998 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
999 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
1000 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
1001 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
1002 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
1003 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
1004 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
1005 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
1006 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
1007 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
1008 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
1009 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
1010 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
1011 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
1012 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
1013 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
1014 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
1015 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
1016 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
1017 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
1018 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
1019 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
1020 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
1021 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
1022 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
1023 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
1024 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
1025 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
1026 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
1027 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
1028};
1029
1030#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
1031
1032static void
1033csum(void)
1034{
1035 unsigned int i;
1036 unsigned short fcs;
1037 unsigned char v;
1038
1039 if (!scanhex(&adrs))
1040 return;
1041 if (!scanhex(&ncsum))
1042 return;
1043 fcs = 0xffff;
1044 for (i = 0; i < ncsum; ++i) {
1045 if (mread(adrs+i, &v, 1) == 0) {
1046 printf("csum stopped at %x\n", adrs+i);
1047 break;
1048 }
1049 fcs = FCS(fcs, v);
1050 }
1051 printf("%x\n", fcs);
1052}
1053
1054/*
1055 * Check if this is a suitable place to put a breakpoint.
1056 */
1057static long check_bp_loc(unsigned long addr)
1058{
1059 unsigned int instr;
1060
1061 addr &= ~3;
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001062 if (!is_kernel_addr(addr)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 printf("Breakpoints may only be placed at kernel addresses\n");
1064 return 0;
1065 }
1066 if (!mread(addr, &instr, sizeof(instr))) {
1067 printf("Can't read instruction at address %lx\n", addr);
1068 return 0;
1069 }
1070 if (IS_MTMSRD(instr) || IS_RFID(instr)) {
1071 printf("Breakpoints may not be placed on mtmsrd or rfid "
1072 "instructions\n");
1073 return 0;
1074 }
1075 return 1;
1076}
1077
1078static char *breakpoint_help_string =
1079 "Breakpoint command usage:\n"
1080 "b show breakpoints\n"
1081 "b <addr> [cnt] set breakpoint at given instr addr\n"
1082 "bc clear all breakpoints\n"
1083 "bc <n/addr> clear breakpoint number n or at addr\n"
1084 "bi <addr> [cnt] set hardware instr breakpoint (POWER3/RS64 only)\n"
1085 "bd <addr> [cnt] set hardware data breakpoint\n"
1086 "";
1087
1088static void
1089bpt_cmds(void)
1090{
1091 int cmd;
1092 unsigned long a;
1093 int mode, i;
1094 struct bpt *bp;
1095 const char badaddr[] = "Only kernel addresses are permitted "
1096 "for breakpoints\n";
1097
1098 cmd = inchar();
1099 switch (cmd) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001100#ifndef CONFIG_8xx
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 case 'd': /* bd - hardware data breakpoint */
1102 mode = 7;
1103 cmd = inchar();
1104 if (cmd == 'r')
1105 mode = 5;
1106 else if (cmd == 'w')
1107 mode = 6;
1108 else
1109 termch = cmd;
1110 dabr.address = 0;
1111 dabr.enabled = 0;
1112 if (scanhex(&dabr.address)) {
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001113 if (!is_kernel_addr(dabr.address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 printf(badaddr);
1115 break;
1116 }
1117 dabr.address &= ~7;
1118 dabr.enabled = mode | BP_DABR;
1119 }
1120 break;
1121
1122 case 'i': /* bi - hardware instr breakpoint */
1123 if (!cpu_has_feature(CPU_FTR_IABR)) {
1124 printf("Hardware instruction breakpoint "
1125 "not supported on this cpu\n");
1126 break;
1127 }
1128 if (iabr) {
1129 iabr->enabled &= ~(BP_IABR | BP_IABR_TE);
1130 iabr = NULL;
1131 }
1132 if (!scanhex(&a))
1133 break;
1134 if (!check_bp_loc(a))
1135 break;
1136 bp = new_breakpoint(a);
1137 if (bp != NULL) {
1138 bp->enabled |= BP_IABR | BP_IABR_TE;
1139 iabr = bp;
1140 }
1141 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001142#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143
1144 case 'c':
1145 if (!scanhex(&a)) {
1146 /* clear all breakpoints */
1147 for (i = 0; i < NBPTS; ++i)
1148 bpts[i].enabled = 0;
1149 iabr = NULL;
1150 dabr.enabled = 0;
1151 printf("All breakpoints cleared\n");
1152 break;
1153 }
1154
1155 if (a <= NBPTS && a >= 1) {
1156 /* assume a breakpoint number */
1157 bp = &bpts[a-1]; /* bp nums are 1 based */
1158 } else {
1159 /* assume a breakpoint address */
1160 bp = at_breakpoint(a);
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001161 if (bp == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 printf("No breakpoint at %x\n", a);
1163 break;
1164 }
1165 }
1166
1167 printf("Cleared breakpoint %x (", BP_NUM(bp));
1168 xmon_print_symbol(bp->address, " ", ")\n");
1169 bp->enabled = 0;
1170 break;
1171
1172 default:
1173 termch = cmd;
1174 cmd = skipbl();
1175 if (cmd == '?') {
1176 printf(breakpoint_help_string);
1177 break;
1178 }
1179 termch = cmd;
1180 if (!scanhex(&a)) {
1181 /* print all breakpoints */
1182 printf(" type address\n");
1183 if (dabr.enabled) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001184 printf(" data "REG" [", dabr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 if (dabr.enabled & 1)
1186 printf("r");
1187 if (dabr.enabled & 2)
1188 printf("w");
1189 printf("]\n");
1190 }
1191 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
1192 if (!bp->enabled)
1193 continue;
1194 printf("%2x %s ", BP_NUM(bp),
1195 (bp->enabled & BP_IABR)? "inst": "trap");
1196 xmon_print_symbol(bp->address, " ", "\n");
1197 }
1198 break;
1199 }
1200
1201 if (!check_bp_loc(a))
1202 break;
1203 bp = new_breakpoint(a);
1204 if (bp != NULL)
1205 bp->enabled |= BP_TRAP;
1206 break;
1207 }
1208}
1209
1210/* Very cheap human name for vector lookup. */
1211static
1212const char *getvecname(unsigned long vec)
1213{
1214 char *ret;
1215
1216 switch (vec) {
1217 case 0x100: ret = "(System Reset)"; break;
1218 case 0x200: ret = "(Machine Check)"; break;
1219 case 0x300: ret = "(Data Access)"; break;
1220 case 0x380: ret = "(Data SLB Access)"; break;
1221 case 0x400: ret = "(Instruction Access)"; break;
1222 case 0x480: ret = "(Instruction SLB Access)"; break;
1223 case 0x500: ret = "(Hardware Interrupt)"; break;
1224 case 0x600: ret = "(Alignment)"; break;
1225 case 0x700: ret = "(Program Check)"; break;
1226 case 0x800: ret = "(FPU Unavailable)"; break;
1227 case 0x900: ret = "(Decrementer)"; break;
1228 case 0xc00: ret = "(System Call)"; break;
1229 case 0xd00: ret = "(Single Step)"; break;
1230 case 0xf00: ret = "(Performance Monitor)"; break;
1231 case 0xf20: ret = "(Altivec Unavailable)"; break;
1232 case 0x1300: ret = "(Instruction Breakpoint)"; break;
1233 default: ret = "";
1234 }
1235 return ret;
1236}
1237
1238static void get_function_bounds(unsigned long pc, unsigned long *startp,
1239 unsigned long *endp)
1240{
1241 unsigned long size, offset;
1242 const char *name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
1244 *startp = *endp = 0;
1245 if (pc == 0)
1246 return;
1247 if (setjmp(bus_error_jmp) == 0) {
1248 catch_memory_errors = 1;
1249 sync();
Alexey Dobriyanffb45122007-05-08 00:28:41 -07001250 name = kallsyms_lookup(pc, &size, &offset, NULL, tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 if (name != NULL) {
1252 *startp = pc - offset;
1253 *endp = pc - offset + size;
1254 }
1255 sync();
1256 }
1257 catch_memory_errors = 0;
1258}
1259
1260static int xmon_depth_to_print = 64;
1261
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001262#define LRSAVE_OFFSET (STACK_FRAME_LR_SAVE * sizeof(unsigned long))
1263#define MARKER_OFFSET (STACK_FRAME_MARKER * sizeof(unsigned long))
1264
1265#ifdef __powerpc64__
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001266#define REGS_OFFSET 0x70
1267#else
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001268#define REGS_OFFSET 16
1269#endif
1270
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271static void xmon_show_stack(unsigned long sp, unsigned long lr,
1272 unsigned long pc)
1273{
1274 unsigned long ip;
1275 unsigned long newsp;
1276 unsigned long marker;
1277 int count = 0;
1278 struct pt_regs regs;
1279
1280 do {
1281 if (sp < PAGE_OFFSET) {
1282 if (sp != 0)
1283 printf("SP (%lx) is in userspace\n", sp);
1284 break;
1285 }
1286
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001287 if (!mread(sp + LRSAVE_OFFSET, &ip, sizeof(unsigned long))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 || !mread(sp, &newsp, sizeof(unsigned long))) {
1289 printf("Couldn't read stack frame at %lx\n", sp);
1290 break;
1291 }
1292
1293 /*
1294 * For the first stack frame, try to work out if
1295 * LR and/or the saved LR value in the bottommost
1296 * stack frame are valid.
1297 */
1298 if ((pc | lr) != 0) {
1299 unsigned long fnstart, fnend;
1300 unsigned long nextip;
1301 int printip = 1;
1302
1303 get_function_bounds(pc, &fnstart, &fnend);
1304 nextip = 0;
1305 if (newsp > sp)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001306 mread(newsp + LRSAVE_OFFSET, &nextip,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 sizeof(unsigned long));
1308 if (lr == ip) {
1309 if (lr < PAGE_OFFSET
1310 || (fnstart <= lr && lr < fnend))
1311 printip = 0;
1312 } else if (lr == nextip) {
1313 printip = 0;
1314 } else if (lr >= PAGE_OFFSET
1315 && !(fnstart <= lr && lr < fnend)) {
1316 printf("[link register ] ");
1317 xmon_print_symbol(lr, " ", "\n");
1318 }
1319 if (printip) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001320 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 xmon_print_symbol(ip, " ", " (unreliable)\n");
1322 }
1323 pc = lr = 0;
1324
1325 } else {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001326 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 xmon_print_symbol(ip, " ", "\n");
1328 }
1329
1330 /* Look for "regshere" marker to see if this is
1331 an exception frame. */
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001332 if (mread(sp + MARKER_OFFSET, &marker, sizeof(unsigned long))
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001333 && marker == STACK_FRAME_REGS_MARKER) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001334 if (mread(sp + REGS_OFFSET, &regs, sizeof(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 != sizeof(regs)) {
1336 printf("Couldn't read registers at %lx\n",
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001337 sp + REGS_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 break;
1339 }
1340 printf("--- Exception: %lx %s at ", regs.trap,
1341 getvecname(TRAP(&regs)));
1342 pc = regs.nip;
1343 lr = regs.link;
1344 xmon_print_symbol(pc, " ", "\n");
1345 }
1346
1347 if (newsp == 0)
1348 break;
1349
1350 sp = newsp;
1351 } while (count++ < xmon_depth_to_print);
1352}
1353
1354static void backtrace(struct pt_regs *excp)
1355{
1356 unsigned long sp;
1357
1358 if (scanhex(&sp))
1359 xmon_show_stack(sp, 0, 0);
1360 else
1361 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
1362 scannl();
1363}
1364
1365static void print_bug_trap(struct pt_regs *regs)
1366{
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001367#ifdef CONFIG_BUG
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001368 const struct bug_entry *bug;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 unsigned long addr;
1370
1371 if (regs->msr & MSR_PR)
1372 return; /* not in kernel */
1373 addr = regs->nip; /* address of trap instruction */
1374 if (addr < PAGE_OFFSET)
1375 return;
1376 bug = find_bug(regs->nip);
1377 if (bug == NULL)
1378 return;
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001379 if (is_warning_bug(bug))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 return;
1381
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001382#ifdef CONFIG_DEBUG_BUGVERBOSE
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001383 printf("kernel BUG at %s:%u!\n",
1384 bug->file, bug->line);
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001385#else
1386 printf("kernel BUG at %p!\n", (void *)bug->bug_addr);
1387#endif
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001388#endif /* CONFIG_BUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389}
1390
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001391static void excprint(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392{
1393 unsigned long trap;
1394
1395#ifdef CONFIG_SMP
1396 printf("cpu 0x%x: ", smp_processor_id());
1397#endif /* CONFIG_SMP */
1398
1399 trap = TRAP(fp);
1400 printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(trap), fp);
1401 printf(" pc: ");
1402 xmon_print_symbol(fp->nip, ": ", "\n");
1403
1404 printf(" lr: ", fp->link);
1405 xmon_print_symbol(fp->link, ": ", "\n");
1406
1407 printf(" sp: %lx\n", fp->gpr[1]);
1408 printf(" msr: %lx\n", fp->msr);
1409
1410 if (trap == 0x300 || trap == 0x380 || trap == 0x600) {
1411 printf(" dar: %lx\n", fp->dar);
1412 if (trap != 0x380)
1413 printf(" dsisr: %lx\n", fp->dsisr);
1414 }
1415
1416 printf(" current = 0x%lx\n", current);
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001417#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 printf(" paca = 0x%lx\n", get_paca());
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001419#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 if (current) {
1421 printf(" pid = %ld, comm = %s\n",
1422 current->pid, current->comm);
1423 }
1424
1425 if (trap == 0x700)
1426 print_bug_trap(fp);
1427}
1428
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001429static void prregs(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430{
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001431 int n, trap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 unsigned long base;
1433 struct pt_regs regs;
1434
1435 if (scanhex(&base)) {
1436 if (setjmp(bus_error_jmp) == 0) {
1437 catch_memory_errors = 1;
1438 sync();
1439 regs = *(struct pt_regs *)base;
1440 sync();
1441 __delay(200);
1442 } else {
1443 catch_memory_errors = 0;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001444 printf("*** Error reading registers from "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 base);
1446 return;
1447 }
1448 catch_memory_errors = 0;
1449 fp = &regs;
1450 }
1451
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001452#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 if (FULL_REGS(fp)) {
1454 for (n = 0; n < 16; ++n)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001455 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 n, fp->gpr[n], n+16, fp->gpr[n+16]);
1457 } else {
1458 for (n = 0; n < 7; ++n)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001459 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 n, fp->gpr[n], n+7, fp->gpr[n+7]);
1461 }
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001462#else
1463 for (n = 0; n < 32; ++n) {
1464 printf("R%.2d = %.8x%s", n, fp->gpr[n],
1465 (n & 3) == 3? "\n": " ");
1466 if (n == 12 && !FULL_REGS(fp)) {
1467 printf("\n");
1468 break;
1469 }
1470 }
1471#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 printf("pc = ");
1473 xmon_print_symbol(fp->nip, " ", "\n");
1474 printf("lr = ");
1475 xmon_print_symbol(fp->link, " ", "\n");
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001476 printf("msr = "REG" cr = %.8lx\n", fp->msr, fp->ccr);
1477 printf("ctr = "REG" xer = "REG" trap = %4lx\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 fp->ctr, fp->xer, fp->trap);
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001479 trap = TRAP(fp);
1480 if (trap == 0x300 || trap == 0x380 || trap == 0x600)
1481 printf("dar = "REG" dsisr = %.8lx\n", fp->dar, fp->dsisr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482}
1483
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001484static void cacheflush(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485{
1486 int cmd;
1487 unsigned long nflush;
1488
1489 cmd = inchar();
1490 if (cmd != 'i')
1491 termch = cmd;
1492 scanhex((void *)&adrs);
1493 if (termch != '\n')
1494 termch = 0;
1495 nflush = 1;
1496 scanhex(&nflush);
1497 nflush = (nflush + L1_CACHE_BYTES - 1) / L1_CACHE_BYTES;
1498 if (setjmp(bus_error_jmp) == 0) {
1499 catch_memory_errors = 1;
1500 sync();
1501
1502 if (cmd != 'i') {
1503 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1504 cflush((void *) adrs);
1505 } else {
1506 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1507 cinval((void *) adrs);
1508 }
1509 sync();
1510 /* wait a little while to see if we get a machine check */
1511 __delay(200);
1512 }
1513 catch_memory_errors = 0;
1514}
1515
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001516static unsigned long
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517read_spr(int n)
1518{
1519 unsigned int instrs[2];
1520 unsigned long (*code)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 unsigned long ret = -1UL;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001522#ifdef CONFIG_PPC64
1523 unsigned long opd[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 opd[0] = (unsigned long)instrs;
1526 opd[1] = 0;
1527 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001528 code = (unsigned long (*)(void)) opd;
1529#else
1530 code = (unsigned long (*)(void)) instrs;
1531#endif
1532
1533 /* mfspr r3,n; blr */
1534 instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1535 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 store_inst(instrs);
1537 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538
1539 if (setjmp(bus_error_jmp) == 0) {
1540 catch_memory_errors = 1;
1541 sync();
1542
1543 ret = code();
1544
1545 sync();
1546 /* wait a little while to see if we get a machine check */
1547 __delay(200);
1548 n = size;
1549 }
1550
1551 return ret;
1552}
1553
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001554static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555write_spr(int n, unsigned long val)
1556{
1557 unsigned int instrs[2];
1558 unsigned long (*code)(unsigned long);
Paul Mackerras548cceb2005-11-11 22:36:34 +11001559#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 unsigned long opd[3];
1561
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 opd[0] = (unsigned long)instrs;
1563 opd[1] = 0;
1564 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001565 code = (unsigned long (*)(unsigned long)) opd;
1566#else
1567 code = (unsigned long (*)(unsigned long)) instrs;
1568#endif
1569
1570 instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1571 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 store_inst(instrs);
1573 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574
1575 if (setjmp(bus_error_jmp) == 0) {
1576 catch_memory_errors = 1;
1577 sync();
1578
1579 code(val);
1580
1581 sync();
1582 /* wait a little while to see if we get a machine check */
1583 __delay(200);
1584 n = size;
1585 }
1586}
1587
1588static unsigned long regno;
1589extern char exc_prolog;
1590extern char dec_exc;
1591
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001592static void super_regs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593{
1594 int cmd;
1595 unsigned long val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596
1597 cmd = skipbl();
1598 if (cmd == '\n') {
1599 unsigned long sp, toc;
1600 asm("mr %0,1" : "=r" (sp) :);
1601 asm("mr %0,2" : "=r" (toc) :);
1602
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001603 printf("msr = "REG" sprg0= "REG"\n",
1604 mfmsr(), mfspr(SPRN_SPRG0));
1605 printf("pvr = "REG" sprg1= "REG"\n",
1606 mfspr(SPRN_PVR), mfspr(SPRN_SPRG1));
1607 printf("dec = "REG" sprg2= "REG"\n",
1608 mfspr(SPRN_DEC), mfspr(SPRN_SPRG2));
1609 printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3));
1610 printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611#ifdef CONFIG_PPC_ISERIES
Stephen Rothwell1d135812006-11-13 14:50:28 +11001612 if (firmware_has_feature(FW_FEATURE_ISERIES)) {
1613 struct paca_struct *ptrPaca;
1614 struct lppaca *ptrLpPaca;
Stephen Rothwell1d135812006-11-13 14:50:28 +11001615
1616 /* Dump out relevant Paca data areas. */
1617 printf("Paca: \n");
1618 ptrPaca = get_paca();
1619
1620 printf(" Local Processor Control Area (LpPaca): \n");
1621 ptrLpPaca = ptrPaca->lppaca_ptr;
1622 printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n",
1623 ptrLpPaca->saved_srr0, ptrLpPaca->saved_srr1);
1624 printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n",
1625 ptrLpPaca->saved_gpr3, ptrLpPaca->saved_gpr4);
1626 printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->saved_gpr5);
Stephen Rothwell1d135812006-11-13 14:50:28 +11001627 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628#endif
1629
1630 return;
1631 }
1632
1633 scanhex(&regno);
1634 switch (cmd) {
1635 case 'w':
1636 val = read_spr(regno);
1637 scanhex(&val);
1638 write_spr(regno, val);
1639 /* fall through */
1640 case 'r':
1641 printf("spr %lx = %lx\n", regno, read_spr(regno));
1642 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 }
1644 scannl();
1645}
1646
1647/*
1648 * Stuff for reading and writing memory safely
1649 */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001650static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651mread(unsigned long adrs, void *buf, int size)
1652{
1653 volatile int n;
1654 char *p, *q;
1655
1656 n = 0;
1657 if (setjmp(bus_error_jmp) == 0) {
1658 catch_memory_errors = 1;
1659 sync();
1660 p = (char *)adrs;
1661 q = (char *)buf;
1662 switch (size) {
1663 case 2:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001664 *(u16 *)q = *(u16 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 break;
1666 case 4:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001667 *(u32 *)q = *(u32 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 break;
1669 case 8:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001670 *(u64 *)q = *(u64 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 break;
1672 default:
1673 for( ; n < size; ++n) {
1674 *q++ = *p++;
1675 sync();
1676 }
1677 }
1678 sync();
1679 /* wait a little while to see if we get a machine check */
1680 __delay(200);
1681 n = size;
1682 }
1683 catch_memory_errors = 0;
1684 return n;
1685}
1686
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001687static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688mwrite(unsigned long adrs, void *buf, int size)
1689{
1690 volatile int n;
1691 char *p, *q;
1692
1693 n = 0;
1694 if (setjmp(bus_error_jmp) == 0) {
1695 catch_memory_errors = 1;
1696 sync();
1697 p = (char *) adrs;
1698 q = (char *) buf;
1699 switch (size) {
1700 case 2:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001701 *(u16 *)p = *(u16 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 break;
1703 case 4:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001704 *(u32 *)p = *(u32 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705 break;
1706 case 8:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001707 *(u64 *)p = *(u64 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 break;
1709 default:
1710 for ( ; n < size; ++n) {
1711 *p++ = *q++;
1712 sync();
1713 }
1714 }
1715 sync();
1716 /* wait a little while to see if we get a machine check */
1717 __delay(200);
1718 n = size;
1719 } else {
1720 printf("*** Error writing address %x\n", adrs + n);
1721 }
1722 catch_memory_errors = 0;
1723 return n;
1724}
1725
1726static int fault_type;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001727static int fault_except;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728static char *fault_chars[] = { "--", "**", "##" };
1729
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001730static int handle_fault(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731{
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001732 fault_except = TRAP(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 switch (TRAP(regs)) {
1734 case 0x200:
1735 fault_type = 0;
1736 break;
1737 case 0x300:
1738 case 0x380:
1739 fault_type = 1;
1740 break;
1741 default:
1742 fault_type = 2;
1743 }
1744
1745 longjmp(bus_error_jmp, 1);
1746
1747 return 0;
1748}
1749
1750#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
1751
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001752static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753byterev(unsigned char *val, int size)
1754{
1755 int t;
1756
1757 switch (size) {
1758 case 2:
1759 SWAP(val[0], val[1], t);
1760 break;
1761 case 4:
1762 SWAP(val[0], val[3], t);
1763 SWAP(val[1], val[2], t);
1764 break;
1765 case 8: /* is there really any use for this? */
1766 SWAP(val[0], val[7], t);
1767 SWAP(val[1], val[6], t);
1768 SWAP(val[2], val[5], t);
1769 SWAP(val[3], val[4], t);
1770 break;
1771 }
1772}
1773
1774static int brev;
1775static int mnoread;
1776
1777static char *memex_help_string =
1778 "Memory examine command usage:\n"
1779 "m [addr] [flags] examine/change memory\n"
1780 " addr is optional. will start where left off.\n"
1781 " flags may include chars from this set:\n"
1782 " b modify by bytes (default)\n"
1783 " w modify by words (2 byte)\n"
1784 " l modify by longs (4 byte)\n"
1785 " d modify by doubleword (8 byte)\n"
1786 " r toggle reverse byte order mode\n"
1787 " n do not read memory (for i/o spaces)\n"
1788 " . ok to read (default)\n"
1789 "NOTE: flags are saved as defaults\n"
1790 "";
1791
1792static char *memex_subcmd_help_string =
1793 "Memory examine subcommands:\n"
1794 " hexval write this val to current location\n"
1795 " 'string' write chars from string to this location\n"
1796 " ' increment address\n"
1797 " ^ decrement address\n"
1798 " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n"
1799 " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n"
1800 " ` clear no-read flag\n"
1801 " ; stay at this addr\n"
1802 " v change to byte mode\n"
1803 " w change to word (2 byte) mode\n"
1804 " l change to long (4 byte) mode\n"
1805 " u change to doubleword (8 byte) mode\n"
1806 " m addr change current addr\n"
1807 " n toggle no-read flag\n"
1808 " r toggle byte reverse flag\n"
1809 " < count back up count bytes\n"
1810 " > count skip forward count bytes\n"
1811 " x exit this mode\n"
1812 "";
1813
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001814static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815memex(void)
1816{
1817 int cmd, inc, i, nslash;
1818 unsigned long n;
1819 unsigned char val[16];
1820
1821 scanhex((void *)&adrs);
1822 cmd = skipbl();
1823 if (cmd == '?') {
1824 printf(memex_help_string);
1825 return;
1826 } else {
1827 termch = cmd;
1828 }
1829 last_cmd = "m\n";
1830 while ((cmd = skipbl()) != '\n') {
1831 switch( cmd ){
1832 case 'b': size = 1; break;
1833 case 'w': size = 2; break;
1834 case 'l': size = 4; break;
1835 case 'd': size = 8; break;
1836 case 'r': brev = !brev; break;
1837 case 'n': mnoread = 1; break;
1838 case '.': mnoread = 0; break;
1839 }
1840 }
1841 if( size <= 0 )
1842 size = 1;
1843 else if( size > 8 )
1844 size = 8;
1845 for(;;){
1846 if (!mnoread)
1847 n = mread(adrs, val, size);
Paul Mackerrase1449ed92005-11-10 14:30:20 +11001848 printf(REG"%c", adrs, brev? 'r': ' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849 if (!mnoread) {
1850 if (brev)
1851 byterev(val, size);
1852 putchar(' ');
1853 for (i = 0; i < n; ++i)
1854 printf("%.2x", val[i]);
1855 for (; i < size; ++i)
1856 printf("%s", fault_chars[fault_type]);
1857 }
1858 putchar(' ');
1859 inc = size;
1860 nslash = 0;
1861 for(;;){
1862 if( scanhex(&n) ){
1863 for (i = 0; i < size; ++i)
1864 val[i] = n >> (i * 8);
1865 if (!brev)
1866 byterev(val, size);
1867 mwrite(adrs, val, size);
1868 inc = size;
1869 }
1870 cmd = skipbl();
1871 if (cmd == '\n')
1872 break;
1873 inc = 0;
1874 switch (cmd) {
1875 case '\'':
1876 for(;;){
1877 n = inchar();
1878 if( n == '\\' )
1879 n = bsesc();
1880 else if( n == '\'' )
1881 break;
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 adrs += size;
1888 }
1889 adrs -= size;
1890 inc = size;
1891 break;
1892 case ',':
1893 adrs += size;
1894 break;
1895 case '.':
1896 mnoread = 0;
1897 break;
1898 case ';':
1899 break;
1900 case 'x':
1901 case EOF:
1902 scannl();
1903 return;
1904 case 'b':
1905 case 'v':
1906 size = 1;
1907 break;
1908 case 'w':
1909 size = 2;
1910 break;
1911 case 'l':
1912 size = 4;
1913 break;
1914 case 'u':
1915 size = 8;
1916 break;
1917 case '^':
1918 adrs -= size;
1919 break;
1920 break;
1921 case '/':
1922 if (nslash > 0)
1923 adrs -= 1 << nslash;
1924 else
1925 nslash = 0;
1926 nslash += 4;
1927 adrs += 1 << nslash;
1928 break;
1929 case '\\':
1930 if (nslash < 0)
1931 adrs += 1 << -nslash;
1932 else
1933 nslash = 0;
1934 nslash -= 4;
1935 adrs -= 1 << -nslash;
1936 break;
1937 case 'm':
1938 scanhex((void *)&adrs);
1939 break;
1940 case 'n':
1941 mnoread = 1;
1942 break;
1943 case 'r':
1944 brev = !brev;
1945 break;
1946 case '<':
1947 n = size;
1948 scanhex(&n);
1949 adrs -= n;
1950 break;
1951 case '>':
1952 n = size;
1953 scanhex(&n);
1954 adrs += n;
1955 break;
1956 case '?':
1957 printf(memex_subcmd_help_string);
1958 break;
1959 }
1960 }
1961 adrs += inc;
1962 }
1963}
1964
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001965static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966bsesc(void)
1967{
1968 int c;
1969
1970 c = inchar();
1971 switch( c ){
1972 case 'n': c = '\n'; break;
1973 case 'r': c = '\r'; break;
1974 case 'b': c = '\b'; break;
1975 case 't': c = '\t'; break;
1976 }
1977 return c;
1978}
1979
Olaf Hering7e5b5932006-03-08 20:40:28 +01001980static void xmon_rawdump (unsigned long adrs, long ndump)
1981{
1982 long n, m, r, nr;
1983 unsigned char temp[16];
1984
1985 for (n = ndump; n > 0;) {
1986 r = n < 16? n: 16;
1987 nr = mread(adrs, temp, r);
1988 adrs += nr;
1989 for (m = 0; m < r; ++m) {
1990 if (m < nr)
1991 printf("%.2x", temp[m]);
1992 else
1993 printf("%s", fault_chars[fault_type]);
1994 }
1995 n -= r;
1996 if (nr < r)
1997 break;
1998 }
1999 printf("\n");
2000}
2001
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002#define isxdigit(c) (('0' <= (c) && (c) <= '9') \
2003 || ('a' <= (c) && (c) <= 'f') \
2004 || ('A' <= (c) && (c) <= 'F'))
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002005static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006dump(void)
2007{
2008 int c;
2009
2010 c = inchar();
2011 if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n')
2012 termch = c;
2013 scanhex((void *)&adrs);
2014 if (termch != '\n')
2015 termch = 0;
2016 if (c == 'i') {
2017 scanhex(&nidump);
2018 if (nidump == 0)
2019 nidump = 16;
2020 else if (nidump > MAX_DUMP)
2021 nidump = MAX_DUMP;
2022 adrs += ppc_inst_dump(adrs, nidump, 1);
2023 last_cmd = "di\n";
Vinay Sridharf312deb2009-05-14 23:13:07 +00002024 } else if (c == 'l') {
2025 dump_log_buf();
Olaf Hering7e5b5932006-03-08 20:40:28 +01002026 } else if (c == 'r') {
2027 scanhex(&ndump);
2028 if (ndump == 0)
2029 ndump = 64;
2030 xmon_rawdump(adrs, ndump);
2031 adrs += ndump;
2032 last_cmd = "dr\n";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 } else {
2034 scanhex(&ndump);
2035 if (ndump == 0)
2036 ndump = 64;
2037 else if (ndump > MAX_DUMP)
2038 ndump = MAX_DUMP;
2039 prdump(adrs, ndump);
2040 adrs += ndump;
2041 last_cmd = "d\n";
2042 }
2043}
2044
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002045static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046prdump(unsigned long adrs, long ndump)
2047{
2048 long n, m, c, r, nr;
2049 unsigned char temp[16];
2050
2051 for (n = ndump; n > 0;) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002052 printf(REG, adrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 putchar(' ');
2054 r = n < 16? n: 16;
2055 nr = mread(adrs, temp, r);
2056 adrs += nr;
2057 for (m = 0; m < r; ++m) {
Paul Mackerrase1449ed92005-11-10 14:30:20 +11002058 if ((m & (sizeof(long) - 1)) == 0 && m > 0)
2059 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 if (m < nr)
2061 printf("%.2x", temp[m]);
2062 else
2063 printf("%s", fault_chars[fault_type]);
2064 }
Paul Mackerrase1449ed92005-11-10 14:30:20 +11002065 for (; m < 16; ++m) {
2066 if ((m & (sizeof(long) - 1)) == 0)
2067 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 printf(" ");
Paul Mackerrase1449ed92005-11-10 14:30:20 +11002069 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 printf(" |");
2071 for (m = 0; m < r; ++m) {
2072 if (m < nr) {
2073 c = temp[m];
2074 putchar(' ' <= c && c <= '~'? c: '.');
2075 } else
2076 putchar(' ');
2077 }
2078 n -= r;
2079 for (; m < 16; ++m)
2080 putchar(' ');
2081 printf("|\n");
2082 if (nr < r)
2083 break;
2084 }
2085}
2086
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002087typedef int (*instruction_dump_func)(unsigned long inst, unsigned long addr);
2088
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002089static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002090generic_inst_dump(unsigned long adr, long count, int praddr,
2091 instruction_dump_func dump_func)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092{
2093 int nr, dotted;
2094 unsigned long first_adr;
2095 unsigned long inst, last_inst = 0;
2096 unsigned char val[4];
2097
2098 dotted = 0;
2099 for (first_adr = adr; count > 0; --count, adr += 4) {
2100 nr = mread(adr, val, 4);
2101 if (nr == 0) {
2102 if (praddr) {
2103 const char *x = fault_chars[fault_type];
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002104 printf(REG" %s%s%s%s\n", adr, x, x, x, x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105 }
2106 break;
2107 }
2108 inst = GETWORD(val);
2109 if (adr > first_adr && inst == last_inst) {
2110 if (!dotted) {
2111 printf(" ...\n");
2112 dotted = 1;
2113 }
2114 continue;
2115 }
2116 dotted = 0;
2117 last_inst = inst;
2118 if (praddr)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002119 printf(REG" %.8x", adr, inst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 printf("\t");
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002121 dump_func(inst, adr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 printf("\n");
2123 }
2124 return adr - first_adr;
2125}
2126
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002127static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002128ppc_inst_dump(unsigned long adr, long count, int praddr)
2129{
2130 return generic_inst_dump(adr, count, praddr, print_insn_powerpc);
2131}
2132
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133void
2134print_address(unsigned long addr)
2135{
2136 xmon_print_symbol(addr, "\t# ", "");
2137}
2138
Vinay Sridharf312deb2009-05-14 23:13:07 +00002139void
2140dump_log_buf(void)
2141{
2142 const unsigned long size = 128;
Stephen Rothwell6d1386d2009-06-02 18:15:33 +00002143 unsigned long end, addr;
Vinay Sridharf312deb2009-05-14 23:13:07 +00002144 unsigned char buf[size + 1];
2145
2146 addr = 0;
2147 buf[size] = '\0';
2148
2149 if (setjmp(bus_error_jmp) != 0) {
2150 printf("Unable to lookup symbol __log_buf!\n");
2151 return;
2152 }
2153
2154 catch_memory_errors = 1;
2155 sync();
2156 addr = kallsyms_lookup_name("__log_buf");
2157
2158 if (! addr)
2159 printf("Symbol __log_buf not found!\n");
2160 else {
2161 end = addr + (1 << CONFIG_LOG_BUF_SHIFT);
2162 while (addr < end) {
2163 if (! mread(addr, buf, size)) {
2164 printf("Can't read memory at address 0x%lx\n", addr);
2165 break;
2166 }
2167
2168 printf("%s", buf);
2169
2170 if (strlen(buf) < size)
2171 break;
2172
2173 addr += size;
2174 }
2175 }
2176
2177 sync();
2178 /* wait a little while to see if we get a machine check */
2179 __delay(200);
2180 catch_memory_errors = 0;
2181}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182
2183/*
2184 * Memory operations - move, set, print differences
2185 */
2186static unsigned long mdest; /* destination address */
2187static unsigned long msrc; /* source address */
2188static unsigned long mval; /* byte value to set memory to */
2189static unsigned long mcount; /* # bytes to affect */
2190static unsigned long mdiffs; /* max # differences to print */
2191
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002192static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193memops(int cmd)
2194{
2195 scanhex((void *)&mdest);
2196 if( termch != '\n' )
2197 termch = 0;
2198 scanhex((void *)(cmd == 's'? &mval: &msrc));
2199 if( termch != '\n' )
2200 termch = 0;
2201 scanhex((void *)&mcount);
2202 switch( cmd ){
2203 case 'm':
2204 memmove((void *)mdest, (void *)msrc, mcount);
2205 break;
2206 case 's':
2207 memset((void *)mdest, mval, mcount);
2208 break;
2209 case 'd':
2210 if( termch != '\n' )
2211 termch = 0;
2212 scanhex((void *)&mdiffs);
2213 memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs);
2214 break;
2215 }
2216}
2217
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002218static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr)
2220{
2221 unsigned n, prt;
2222
2223 prt = 0;
2224 for( n = nb; n > 0; --n )
2225 if( *p1++ != *p2++ )
2226 if( ++prt <= maxpr )
2227 printf("%.16x %.2x # %.16x %.2x\n", p1 - 1,
2228 p1[-1], p2 - 1, p2[-1]);
2229 if( prt > maxpr )
2230 printf("Total of %d differences\n", prt);
2231}
2232
2233static unsigned mend;
2234static unsigned mask;
2235
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002236static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237memlocate(void)
2238{
2239 unsigned a, n;
2240 unsigned char val[4];
2241
2242 last_cmd = "ml";
2243 scanhex((void *)&mdest);
2244 if (termch != '\n') {
2245 termch = 0;
2246 scanhex((void *)&mend);
2247 if (termch != '\n') {
2248 termch = 0;
2249 scanhex((void *)&mval);
2250 mask = ~0;
2251 if (termch != '\n') termch = 0;
2252 scanhex((void *)&mask);
2253 }
2254 }
2255 n = 0;
2256 for (a = mdest; a < mend; a += 4) {
2257 if (mread(a, val, 4) == 4
2258 && ((GETWORD(val) ^ mval) & mask) == 0) {
2259 printf("%.16x: %.16x\n", a, GETWORD(val));
2260 if (++n >= 10)
2261 break;
2262 }
2263 }
2264}
2265
2266static unsigned long mskip = 0x1000;
2267static unsigned long mlim = 0xffffffff;
2268
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002269static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270memzcan(void)
2271{
2272 unsigned char v;
2273 unsigned a;
2274 int ok, ook;
2275
2276 scanhex(&mdest);
2277 if (termch != '\n') termch = 0;
2278 scanhex(&mskip);
2279 if (termch != '\n') termch = 0;
2280 scanhex(&mlim);
2281 ook = 0;
2282 for (a = mdest; a < mlim; a += mskip) {
2283 ok = mread(a, &v, 1);
2284 if (ok && !ook) {
2285 printf("%.8x .. ", a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 } else if (!ok && ook)
2287 printf("%.8x\n", a - mskip);
2288 ook = ok;
2289 if (a + mskip < a)
2290 break;
2291 }
2292 if (ook)
2293 printf("%.8x\n", a - mskip);
2294}
2295
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002296static void proccall(void)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002297{
2298 unsigned long args[8];
2299 unsigned long ret;
2300 int i;
2301 typedef unsigned long (*callfunc_t)(unsigned long, unsigned long,
2302 unsigned long, unsigned long, unsigned long,
2303 unsigned long, unsigned long, unsigned long);
2304 callfunc_t func;
2305
2306 if (!scanhex(&adrs))
2307 return;
2308 if (termch != '\n')
2309 termch = 0;
2310 for (i = 0; i < 8; ++i)
2311 args[i] = 0;
2312 for (i = 0; i < 8; ++i) {
2313 if (!scanhex(&args[i]) || termch == '\n')
2314 break;
2315 termch = 0;
2316 }
2317 func = (callfunc_t) adrs;
2318 ret = 0;
2319 if (setjmp(bus_error_jmp) == 0) {
2320 catch_memory_errors = 1;
2321 sync();
2322 ret = func(args[0], args[1], args[2], args[3],
2323 args[4], args[5], args[6], args[7]);
2324 sync();
2325 printf("return value is %x\n", ret);
2326 } else {
2327 printf("*** %x exception occurred\n", fault_except);
2328 }
2329 catch_memory_errors = 0;
2330}
2331
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332/* Input scanning routines */
2333int
2334skipbl(void)
2335{
2336 int c;
2337
2338 if( termch != 0 ){
2339 c = termch;
2340 termch = 0;
2341 } else
2342 c = inchar();
2343 while( c == ' ' || c == '\t' )
2344 c = inchar();
2345 return c;
2346}
2347
2348#define N_PTREGS 44
2349static char *regnames[N_PTREGS] = {
2350 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
2351 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
2352 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
2353 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002354 "pc", "msr", "or3", "ctr", "lr", "xer", "ccr",
2355#ifdef CONFIG_PPC64
2356 "softe",
2357#else
2358 "mq",
2359#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360 "trap", "dar", "dsisr", "res"
2361};
2362
2363int
2364scanhex(unsigned long *vp)
2365{
2366 int c, d;
2367 unsigned long v;
2368
2369 c = skipbl();
2370 if (c == '%') {
2371 /* parse register name */
2372 char regname[8];
2373 int i;
2374
2375 for (i = 0; i < sizeof(regname) - 1; ++i) {
2376 c = inchar();
2377 if (!isalnum(c)) {
2378 termch = c;
2379 break;
2380 }
2381 regname[i] = c;
2382 }
2383 regname[i] = 0;
2384 for (i = 0; i < N_PTREGS; ++i) {
2385 if (strcmp(regnames[i], regname) == 0) {
2386 if (xmon_regs == NULL) {
2387 printf("regs not available\n");
2388 return 0;
2389 }
2390 *vp = ((unsigned long *)xmon_regs)[i];
2391 return 1;
2392 }
2393 }
2394 printf("invalid register name '%%%s'\n", regname);
2395 return 0;
2396 }
2397
2398 /* skip leading "0x" if any */
2399
2400 if (c == '0') {
2401 c = inchar();
2402 if (c == 'x') {
2403 c = inchar();
2404 } else {
2405 d = hexdigit(c);
2406 if (d == EOF) {
2407 termch = c;
2408 *vp = 0;
2409 return 1;
2410 }
2411 }
2412 } else if (c == '$') {
2413 int i;
2414 for (i=0; i<63; i++) {
2415 c = inchar();
2416 if (isspace(c)) {
2417 termch = c;
2418 break;
2419 }
2420 tmpstr[i] = c;
2421 }
2422 tmpstr[i++] = 0;
Benjamin Herrenschmidt6879dc12005-06-21 17:15:30 -07002423 *vp = 0;
2424 if (setjmp(bus_error_jmp) == 0) {
2425 catch_memory_errors = 1;
2426 sync();
2427 *vp = kallsyms_lookup_name(tmpstr);
2428 sync();
2429 }
2430 catch_memory_errors = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431 if (!(*vp)) {
2432 printf("unknown symbol '%s'\n", tmpstr);
2433 return 0;
2434 }
2435 return 1;
2436 }
2437
2438 d = hexdigit(c);
2439 if (d == EOF) {
2440 termch = c;
2441 return 0;
2442 }
2443 v = 0;
2444 do {
2445 v = (v << 4) + d;
2446 c = inchar();
2447 d = hexdigit(c);
2448 } while (d != EOF);
2449 termch = c;
2450 *vp = v;
2451 return 1;
2452}
2453
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002454static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455scannl(void)
2456{
2457 int c;
2458
2459 c = termch;
2460 termch = 0;
2461 while( c != '\n' )
2462 c = inchar();
2463}
2464
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002465static int hexdigit(int c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466{
2467 if( '0' <= c && c <= '9' )
2468 return c - '0';
2469 if( 'A' <= c && c <= 'F' )
2470 return c - ('A' - 10);
2471 if( 'a' <= c && c <= 'f' )
2472 return c - ('a' - 10);
2473 return EOF;
2474}
2475
2476void
2477getstring(char *s, int size)
2478{
2479 int c;
2480
2481 c = skipbl();
2482 do {
2483 if( size > 1 ){
2484 *s++ = c;
2485 --size;
2486 }
2487 c = inchar();
2488 } while( c != ' ' && c != '\t' && c != '\n' );
2489 termch = c;
2490 *s = 0;
2491}
2492
2493static char line[256];
2494static char *lineptr;
2495
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002496static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497flush_input(void)
2498{
2499 lineptr = NULL;
2500}
2501
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002502static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503inchar(void)
2504{
2505 if (lineptr == NULL || *lineptr == 0) {
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002506 if (xmon_gets(line, sizeof(line)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 lineptr = NULL;
2508 return EOF;
2509 }
2510 lineptr = line;
2511 }
2512 return *lineptr++;
2513}
2514
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002515static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516take_input(char *str)
2517{
2518 lineptr = str;
2519}
2520
2521
2522static void
2523symbol_lookup(void)
2524{
2525 int type = inchar();
2526 unsigned long addr;
2527 static char tmp[64];
2528
2529 switch (type) {
2530 case 'a':
2531 if (scanhex(&addr))
2532 xmon_print_symbol(addr, ": ", "\n");
2533 termch = 0;
2534 break;
2535 case 's':
2536 getstring(tmp, 64);
2537 if (setjmp(bus_error_jmp) == 0) {
2538 catch_memory_errors = 1;
2539 sync();
2540 addr = kallsyms_lookup_name(tmp);
2541 if (addr)
2542 printf("%s: %lx\n", tmp, addr);
2543 else
2544 printf("Symbol '%s' not found.\n", tmp);
2545 sync();
2546 }
2547 catch_memory_errors = 0;
2548 termch = 0;
2549 break;
2550 }
2551}
2552
2553
2554/* Print an address in numeric and symbolic form (if possible) */
2555static void xmon_print_symbol(unsigned long address, const char *mid,
2556 const char *after)
2557{
2558 char *modname;
2559 const char *name = NULL;
2560 unsigned long offset, size;
2561
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002562 printf(REG, address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563 if (setjmp(bus_error_jmp) == 0) {
2564 catch_memory_errors = 1;
2565 sync();
2566 name = kallsyms_lookup(address, &size, &offset, &modname,
2567 tmpstr);
2568 sync();
2569 /* wait a little while to see if we get a machine check */
2570 __delay(200);
2571 }
2572
2573 catch_memory_errors = 0;
2574
2575 if (name) {
2576 printf("%s%s+%#lx/%#lx", mid, name, offset, size);
2577 if (modname)
2578 printf(" [%s]", modname);
2579 }
2580 printf("%s", after);
2581}
2582
Benjamin Herrenschmidt2d27cfd2009-07-23 23:15:59 +00002583#ifdef CONFIG_PPC_BOOK3S_64
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584static void dump_slb(void)
2585{
2586 int i;
will schmidtb3b95952007-12-07 08:22:23 +11002587 unsigned long esid,vsid,valid;
2588 unsigned long llp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589
2590 printf("SLB contents of cpu %x\n", smp_processor_id());
2591
Michael Neuling584f8b72007-12-06 17:24:48 +11002592 for (i = 0; i < mmu_slb_size; i++) {
will schmidtb3b95952007-12-07 08:22:23 +11002593 asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i));
2594 asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i));
2595 valid = (esid & SLB_ESID_V);
2596 if (valid | esid | vsid) {
2597 printf("%02d %016lx %016lx", i, esid, vsid);
2598 if (valid) {
2599 llp = vsid & SLB_VSID_LLP;
2600 if (vsid & SLB_VSID_B_1T) {
2601 printf(" 1T ESID=%9lx VSID=%13lx LLP:%3lx \n",
2602 GET_ESID_1T(esid),
2603 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT_1T,
2604 llp);
2605 } else {
2606 printf(" 256M ESID=%9lx VSID=%13lx LLP:%3lx \n",
2607 GET_ESID(esid),
2608 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT,
2609 llp);
2610 }
2611 } else
2612 printf("\n");
2613 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 }
2615}
2616
2617static void dump_stab(void)
2618{
2619 int i;
2620 unsigned long *tmp = (unsigned long *)get_paca()->stab_addr;
2621
2622 printf("Segment table contents of cpu %x\n", smp_processor_id());
2623
2624 for (i = 0; i < PAGE_SIZE/16; i++) {
2625 unsigned long a, b;
2626
2627 a = *tmp++;
2628 b = *tmp++;
2629
2630 if (a || b) {
2631 printf("%03d %016lx ", i, a);
2632 printf("%016lx\n", b);
2633 }
2634 }
2635}
2636
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002637void dump_segments(void)
2638{
2639 if (cpu_has_feature(CPU_FTR_SLB))
2640 dump_slb();
2641 else
2642 dump_stab();
2643}
2644#endif
2645
2646#ifdef CONFIG_PPC_STD_MMU_32
2647void dump_segments(void)
2648{
2649 int i;
2650
2651 printf("sr0-15 =");
2652 for (i = 0; i < 16; ++i)
2653 printf(" %x", mfsrin(i));
2654 printf("\n");
2655}
2656#endif
2657
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +11002658#ifdef CONFIG_44x
2659static void dump_tlb_44x(void)
2660{
2661 int i;
2662
2663 for (i = 0; i < PPC44x_TLB_SIZE; i++) {
2664 unsigned long w0,w1,w2;
2665 asm volatile("tlbre %0,%1,0" : "=r" (w0) : "r" (i));
2666 asm volatile("tlbre %0,%1,1" : "=r" (w1) : "r" (i));
2667 asm volatile("tlbre %0,%1,2" : "=r" (w2) : "r" (i));
2668 printf("[%02x] %08x %08x %08x ", i, w0, w1, w2);
2669 if (w0 & PPC44x_TLB_VALID) {
2670 printf("V %08x -> %01x%08x %c%c%c%c%c",
2671 w0 & PPC44x_TLB_EPN_MASK,
2672 w1 & PPC44x_TLB_ERPN_MASK,
2673 w1 & PPC44x_TLB_RPN_MASK,
2674 (w2 & PPC44x_TLB_W) ? 'W' : 'w',
2675 (w2 & PPC44x_TLB_I) ? 'I' : 'i',
2676 (w2 & PPC44x_TLB_M) ? 'M' : 'm',
2677 (w2 & PPC44x_TLB_G) ? 'G' : 'g',
2678 (w2 & PPC44x_TLB_E) ? 'E' : 'e');
2679 }
2680 printf("\n");
2681 }
2682}
2683#endif /* CONFIG_44x */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002684
2685static void xmon_init(int enable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686{
Stephen Rothwellbbb68172006-11-30 11:44:09 +11002687#ifdef CONFIG_PPC_ISERIES
2688 if (firmware_has_feature(FW_FEATURE_ISERIES))
2689 return;
2690#endif
Olaf Heringb13cfd172005-08-04 19:26:42 +02002691 if (enable) {
2692 __debugger = xmon;
2693 __debugger_ipi = xmon_ipi;
2694 __debugger_bpt = xmon_bpt;
2695 __debugger_sstep = xmon_sstep;
2696 __debugger_iabr_match = xmon_iabr_match;
2697 __debugger_dabr_match = xmon_dabr_match;
2698 __debugger_fault_handler = xmon_fault_handler;
2699 } else {
2700 __debugger = NULL;
2701 __debugger_ipi = NULL;
2702 __debugger_bpt = NULL;
2703 __debugger_sstep = NULL;
2704 __debugger_iabr_match = NULL;
2705 __debugger_dabr_match = NULL;
2706 __debugger_fault_handler = NULL;
2707 }
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002708 xmon_map_scc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709}
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002710
2711#ifdef CONFIG_MAGIC_SYSRQ
David Howells7d12e782006-10-05 14:55:46 +01002712static void sysrq_handle_xmon(int key, struct tty_struct *tty)
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002713{
2714 /* ensure xmon is enabled */
2715 xmon_init(1);
David Howells7d12e782006-10-05 14:55:46 +01002716 debugger(get_irq_regs());
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002717}
2718
2719static struct sysrq_key_op sysrq_xmon_op =
2720{
2721 .handler = sysrq_handle_xmon,
2722 .help_msg = "Xmon",
2723 .action_msg = "Entering xmon",
2724};
2725
2726static int __init setup_xmon_sysrq(void)
2727{
Stephen Rothwellbbb68172006-11-30 11:44:09 +11002728#ifdef CONFIG_PPC_ISERIES
2729 if (firmware_has_feature(FW_FEATURE_ISERIES))
2730 return 0;
2731#endif
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002732 register_sysrq_key('x', &sysrq_xmon_op);
2733 return 0;
2734}
2735__initcall(setup_xmon_sysrq);
2736#endif /* CONFIG_MAGIC_SYSRQ */
Michael Ellerman476792832006-10-03 14:12:08 +10002737
Olaf Heringf5e6a282007-06-24 16:57:08 +10002738static int __initdata xmon_early, xmon_off;
Michael Ellerman476792832006-10-03 14:12:08 +10002739
2740static int __init early_parse_xmon(char *p)
2741{
2742 if (!p || strncmp(p, "early", 5) == 0) {
2743 /* just "xmon" is equivalent to "xmon=early" */
2744 xmon_init(1);
2745 xmon_early = 1;
2746 } else if (strncmp(p, "on", 2) == 0)
2747 xmon_init(1);
2748 else if (strncmp(p, "off", 3) == 0)
2749 xmon_off = 1;
2750 else if (strncmp(p, "nobt", 4) == 0)
2751 xmon_no_auto_backtrace = 1;
2752 else
2753 return 1;
2754
2755 return 0;
2756}
2757early_param("xmon", early_parse_xmon);
2758
2759void __init xmon_setup(void)
2760{
2761#ifdef CONFIG_XMON_DEFAULT
2762 if (!xmon_off)
2763 xmon_init(1);
2764#endif
2765 if (xmon_early)
2766 debugger(NULL);
2767}
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002768
Arnd Bergmanne055595d2006-11-27 19:18:55 +01002769#ifdef CONFIG_SPU_BASE
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002770
2771struct spu_info {
2772 struct spu *spu;
2773 u64 saved_mfc_sr1_RW;
2774 u32 saved_spu_runcntl_RW;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002775 unsigned long dump_addr;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002776 u8 stopped_ok;
2777};
2778
2779#define XMON_NUM_SPUS 16 /* Enough for current hardware */
2780
2781static struct spu_info spu_info[XMON_NUM_SPUS];
2782
2783void xmon_register_spus(struct list_head *list)
2784{
2785 struct spu *spu;
2786
2787 list_for_each_entry(spu, list, full_list) {
2788 if (spu->number >= XMON_NUM_SPUS) {
2789 WARN_ON(1);
2790 continue;
2791 }
2792
2793 spu_info[spu->number].spu = spu;
2794 spu_info[spu->number].stopped_ok = 0;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002795 spu_info[spu->number].dump_addr = (unsigned long)
2796 spu_info[spu->number].spu->local_store;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002797 }
2798}
2799
2800static void stop_spus(void)
2801{
2802 struct spu *spu;
2803 int i;
2804 u64 tmp;
2805
2806 for (i = 0; i < XMON_NUM_SPUS; i++) {
2807 if (!spu_info[i].spu)
2808 continue;
2809
2810 if (setjmp(bus_error_jmp) == 0) {
2811 catch_memory_errors = 1;
2812 sync();
2813
2814 spu = spu_info[i].spu;
2815
2816 spu_info[i].saved_spu_runcntl_RW =
2817 in_be32(&spu->problem->spu_runcntl_RW);
2818
2819 tmp = spu_mfc_sr1_get(spu);
2820 spu_info[i].saved_mfc_sr1_RW = tmp;
2821
2822 tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
2823 spu_mfc_sr1_set(spu, tmp);
2824
2825 sync();
2826 __delay(200);
2827
2828 spu_info[i].stopped_ok = 1;
Michael Ellerman2a144422006-11-23 00:46:40 +01002829
2830 printf("Stopped spu %.2d (was %s)\n", i,
2831 spu_info[i].saved_spu_runcntl_RW ?
2832 "running" : "stopped");
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002833 } else {
2834 catch_memory_errors = 0;
2835 printf("*** Error stopping spu %.2d\n", i);
2836 }
2837 catch_memory_errors = 0;
2838 }
2839}
2840
2841static void restart_spus(void)
2842{
2843 struct spu *spu;
2844 int i;
2845
2846 for (i = 0; i < XMON_NUM_SPUS; i++) {
2847 if (!spu_info[i].spu)
2848 continue;
2849
2850 if (!spu_info[i].stopped_ok) {
2851 printf("*** Error, spu %d was not successfully stopped"
2852 ", not restarting\n", i);
2853 continue;
2854 }
2855
2856 if (setjmp(bus_error_jmp) == 0) {
2857 catch_memory_errors = 1;
2858 sync();
2859
2860 spu = spu_info[i].spu;
2861 spu_mfc_sr1_set(spu, spu_info[i].saved_mfc_sr1_RW);
2862 out_be32(&spu->problem->spu_runcntl_RW,
2863 spu_info[i].saved_spu_runcntl_RW);
2864
2865 sync();
2866 __delay(200);
2867
2868 printf("Restarted spu %.2d\n", i);
2869 } else {
2870 catch_memory_errors = 0;
2871 printf("*** Error restarting spu %.2d\n", i);
2872 }
2873 catch_memory_errors = 0;
2874 }
2875}
2876
Michael Ellermana8984972006-10-24 18:31:28 +02002877#define DUMP_WIDTH 23
Michael Ellerman437a0702006-11-23 00:46:39 +01002878#define DUMP_VALUE(format, field, value) \
Michael Ellermana8984972006-10-24 18:31:28 +02002879do { \
2880 if (setjmp(bus_error_jmp) == 0) { \
2881 catch_memory_errors = 1; \
2882 sync(); \
2883 printf(" %-*s = "format"\n", DUMP_WIDTH, \
Michael Ellerman437a0702006-11-23 00:46:39 +01002884 #field, value); \
Michael Ellermana8984972006-10-24 18:31:28 +02002885 sync(); \
2886 __delay(200); \
2887 } else { \
2888 catch_memory_errors = 0; \
2889 printf(" %-*s = *** Error reading field.\n", \
2890 DUMP_WIDTH, #field); \
2891 } \
2892 catch_memory_errors = 0; \
2893} while (0)
2894
Michael Ellerman437a0702006-11-23 00:46:39 +01002895#define DUMP_FIELD(obj, format, field) \
2896 DUMP_VALUE(format, field, obj->field)
2897
Michael Ellermana8984972006-10-24 18:31:28 +02002898static void dump_spu_fields(struct spu *spu)
2899{
2900 printf("Dumping spu fields at address %p:\n", spu);
2901
2902 DUMP_FIELD(spu, "0x%x", number);
2903 DUMP_FIELD(spu, "%s", name);
Michael Ellermana8984972006-10-24 18:31:28 +02002904 DUMP_FIELD(spu, "0x%lx", local_store_phys);
2905 DUMP_FIELD(spu, "0x%p", local_store);
2906 DUMP_FIELD(spu, "0x%lx", ls_size);
2907 DUMP_FIELD(spu, "0x%x", node);
2908 DUMP_FIELD(spu, "0x%lx", flags);
Michael Ellermana8984972006-10-24 18:31:28 +02002909 DUMP_FIELD(spu, "%d", class_0_pending);
Luke Browningf3d69e02008-04-27 18:41:55 +00002910 DUMP_FIELD(spu, "0x%lx", class_0_dar);
Luke Browningf3d69e02008-04-27 18:41:55 +00002911 DUMP_FIELD(spu, "0x%lx", class_1_dar);
2912 DUMP_FIELD(spu, "0x%lx", class_1_dsisr);
Michael Ellermana8984972006-10-24 18:31:28 +02002913 DUMP_FIELD(spu, "0x%lx", irqs[0]);
2914 DUMP_FIELD(spu, "0x%lx", irqs[1]);
2915 DUMP_FIELD(spu, "0x%lx", irqs[2]);
2916 DUMP_FIELD(spu, "0x%x", slb_replace);
2917 DUMP_FIELD(spu, "%d", pid);
Michael Ellermana8984972006-10-24 18:31:28 +02002918 DUMP_FIELD(spu, "0x%p", mm);
2919 DUMP_FIELD(spu, "0x%p", ctx);
2920 DUMP_FIELD(spu, "0x%p", rq);
2921 DUMP_FIELD(spu, "0x%p", timestamp);
2922 DUMP_FIELD(spu, "0x%lx", problem_phys);
2923 DUMP_FIELD(spu, "0x%p", problem);
Michael Ellerman437a0702006-11-23 00:46:39 +01002924 DUMP_VALUE("0x%x", problem->spu_runcntl_RW,
2925 in_be32(&spu->problem->spu_runcntl_RW));
2926 DUMP_VALUE("0x%x", problem->spu_status_R,
2927 in_be32(&spu->problem->spu_status_R));
2928 DUMP_VALUE("0x%x", problem->spu_npc_RW,
2929 in_be32(&spu->problem->spu_npc_RW));
Michael Ellermana8984972006-10-24 18:31:28 +02002930 DUMP_FIELD(spu, "0x%p", priv2);
Michael Ellermana9852392006-11-23 00:46:50 +01002931 DUMP_FIELD(spu, "0x%p", pdata);
Michael Ellermana8984972006-10-24 18:31:28 +02002932}
2933
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002934int
2935spu_inst_dump(unsigned long adr, long count, int praddr)
2936{
2937 return generic_inst_dump(adr, count, praddr, print_insn_spu);
2938}
2939
2940static void dump_spu_ls(unsigned long num, int subcmd)
Michael Ellerman24a24c82006-11-23 00:46:41 +01002941{
2942 unsigned long offset, addr, ls_addr;
2943
2944 if (setjmp(bus_error_jmp) == 0) {
2945 catch_memory_errors = 1;
2946 sync();
2947 ls_addr = (unsigned long)spu_info[num].spu->local_store;
2948 sync();
2949 __delay(200);
2950 } else {
2951 catch_memory_errors = 0;
2952 printf("*** Error: accessing spu info for spu %d\n", num);
2953 return;
2954 }
2955 catch_memory_errors = 0;
2956
2957 if (scanhex(&offset))
2958 addr = ls_addr + offset;
2959 else
2960 addr = spu_info[num].dump_addr;
2961
2962 if (addr >= ls_addr + LS_SIZE) {
2963 printf("*** Error: address outside of local store\n");
2964 return;
2965 }
2966
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002967 switch (subcmd) {
2968 case 'i':
2969 addr += spu_inst_dump(addr, 16, 1);
2970 last_cmd = "sdi\n";
2971 break;
2972 default:
2973 prdump(addr, 64);
2974 addr += 64;
2975 last_cmd = "sd\n";
2976 break;
2977 }
Michael Ellerman24a24c82006-11-23 00:46:41 +01002978
2979 spu_info[num].dump_addr = addr;
2980}
2981
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002982static int do_spu_cmd(void)
2983{
Michael Ellerman24a24c82006-11-23 00:46:41 +01002984 static unsigned long num = 0;
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002985 int cmd, subcmd = 0;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002986
2987 cmd = inchar();
2988 switch (cmd) {
2989 case 's':
2990 stop_spus();
2991 break;
2992 case 'r':
2993 restart_spus();
2994 break;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002995 case 'd':
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002996 subcmd = inchar();
2997 if (isxdigit(subcmd) || subcmd == '\n')
2998 termch = subcmd;
2999 case 'f':
Michael Ellerman24a24c82006-11-23 00:46:41 +01003000 scanhex(&num);
3001 if (num >= XMON_NUM_SPUS || !spu_info[num].spu) {
Michael Ellermana8984972006-10-24 18:31:28 +02003002 printf("*** Error: invalid spu number\n");
Michael Ellerman24a24c82006-11-23 00:46:41 +01003003 return 0;
3004 }
3005
3006 switch (cmd) {
3007 case 'f':
3008 dump_spu_fields(spu_info[num].spu);
3009 break;
3010 default:
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003011 dump_spu_ls(num, subcmd);
Michael Ellerman24a24c82006-11-23 00:46:41 +01003012 break;
3013 }
3014
Michael Ellermana8984972006-10-24 18:31:28 +02003015 break;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003016 default:
3017 return -1;
3018 }
3019
3020 return 0;
3021}
Arnd Bergmanne055595d2006-11-27 19:18:55 +01003022#else /* ! CONFIG_SPU_BASE */
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003023static int do_spu_cmd(void)
3024{
3025 return -1;
3026}
3027#endif