blob: e1f33a81e5e1f130cf3c1e0b82b4d9d751cf904b [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 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 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
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000338static int xmon_core(struct pt_regs *regs, int fromipi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339{
340 int cmd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 struct bpt *bp;
342 long recurse_jmp[JMP_BUF_LEN];
343 unsigned long offset;
Anton Blanchardf13659e2007-03-21 01:48:34 +1100344 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345#ifdef CONFIG_SMP
346 int cpu;
347 int secondary;
348 unsigned long timeout;
349#endif
350
Anton Blanchardf13659e2007-03-21 01:48:34 +1100351 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
353 bp = in_breakpoint_table(regs->nip, &offset);
354 if (bp != NULL) {
355 regs->nip = bp->address + offset;
356 atomic_dec(&bp->ref_count);
357 }
358
359 remove_cpu_bpts();
360
361#ifdef CONFIG_SMP
362 cpu = smp_processor_id();
363 if (cpu_isset(cpu, cpus_in_xmon)) {
364 get_output_lock();
365 excprint(regs);
366 printf("cpu 0x%x: Exception %lx %s in xmon, "
367 "returning to main loop\n",
368 cpu, regs->trap, getvecname(TRAP(regs)));
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000369 release_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 longjmp(xmon_fault_jmp[cpu], 1);
371 }
372
373 if (setjmp(recurse_jmp) != 0) {
374 if (!in_xmon || !xmon_gate) {
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000375 get_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 printf("xmon: WARNING: bad recursive fault "
377 "on cpu 0x%x\n", cpu);
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000378 release_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 goto waiting;
380 }
381 secondary = !(xmon_taken && cpu == xmon_owner);
382 goto cmdloop;
383 }
384
385 xmon_fault_jmp[cpu] = recurse_jmp;
386 cpu_set(cpu, cpus_in_xmon);
387
388 bp = NULL;
389 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF))
390 bp = at_breakpoint(regs->nip);
391 if (bp || (regs->msr & MSR_RI) == 0)
392 fromipi = 0;
393
394 if (!fromipi) {
395 get_output_lock();
396 excprint(regs);
397 if (bp) {
398 printf("cpu 0x%x stopped at breakpoint 0x%x (",
399 cpu, BP_NUM(bp));
400 xmon_print_symbol(regs->nip, " ", ")\n");
401 }
402 if ((regs->msr & MSR_RI) == 0)
403 printf("WARNING: exception is not recoverable, "
404 "can't continue\n");
405 release_output_lock();
406 }
407
408 waiting:
409 secondary = 1;
410 while (secondary && !xmon_gate) {
411 if (in_xmon == 0) {
412 if (fromipi)
413 goto leave;
414 secondary = test_and_set_bit(0, &in_xmon);
415 }
416 barrier();
417 }
418
419 if (!secondary && !xmon_gate) {
420 /* we are the first cpu to come in */
421 /* interrupt other cpu(s) */
422 int ncpus = num_online_cpus();
423
424 xmon_owner = cpu;
425 mb();
426 if (ncpus > 1) {
427 smp_send_debugger_break(MSG_ALL_BUT_SELF);
428 /* wait for other cpus to come in */
429 for (timeout = 100000000; timeout != 0; --timeout) {
430 if (cpus_weight(cpus_in_xmon) >= ncpus)
431 break;
432 barrier();
433 }
434 }
435 remove_bpts();
436 disable_surveillance();
437 /* for breakpoint or single step, print the current instr. */
438 if (bp || TRAP(regs) == 0xd00)
439 ppc_inst_dump(regs->nip, 1, 0);
440 printf("enter ? for help\n");
441 mb();
442 xmon_gate = 1;
443 barrier();
444 }
445
446 cmdloop:
447 while (in_xmon) {
448 if (secondary) {
449 if (cpu == xmon_owner) {
450 if (!test_and_set_bit(0, &xmon_taken)) {
451 secondary = 0;
452 continue;
453 }
454 /* missed it */
455 while (cpu == xmon_owner)
456 barrier();
457 }
458 barrier();
459 } else {
460 cmd = cmds(regs);
461 if (cmd != 0) {
462 /* exiting xmon */
463 insert_bpts();
464 xmon_gate = 0;
465 wmb();
466 in_xmon = 0;
467 break;
468 }
469 /* have switched to some other cpu */
470 secondary = 1;
471 }
472 }
473 leave:
474 cpu_clear(cpu, cpus_in_xmon);
475 xmon_fault_jmp[cpu] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476#else
477 /* UP is simple... */
478 if (in_xmon) {
479 printf("Exception %lx %s in xmon, returning to main loop\n",
480 regs->trap, getvecname(TRAP(regs)));
481 longjmp(xmon_fault_jmp[0], 1);
482 }
483 if (setjmp(recurse_jmp) == 0) {
484 xmon_fault_jmp[0] = recurse_jmp;
485 in_xmon = 1;
486
487 excprint(regs);
488 bp = at_breakpoint(regs->nip);
489 if (bp) {
490 printf("Stopped at breakpoint %x (", BP_NUM(bp));
491 xmon_print_symbol(regs->nip, " ", ")\n");
492 }
493 if ((regs->msr & MSR_RI) == 0)
494 printf("WARNING: exception is not recoverable, "
495 "can't continue\n");
496 remove_bpts();
497 disable_surveillance();
498 /* for breakpoint or single step, print the current instr. */
499 if (bp || TRAP(regs) == 0xd00)
500 ppc_inst_dump(regs->nip, 1, 0);
501 printf("enter ? for help\n");
502 }
503
504 cmd = cmds(regs);
505
506 insert_bpts();
507 in_xmon = 0;
508#endif
509
510 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
511 bp = at_breakpoint(regs->nip);
512 if (bp != NULL) {
513 int stepped = emulate_step(regs, bp->instr[0]);
514 if (stepped == 0) {
515 regs->nip = (unsigned long) &bp->instr[0];
516 atomic_inc(&bp->ref_count);
517 } else if (stepped < 0) {
518 printf("Couldn't single-step %s instruction\n",
519 (IS_RFID(bp->instr[0])? "rfid": "mtmsrd"));
520 }
521 }
522 }
523
524 insert_cpu_bpts();
525
Anton Blanchardf13659e2007-03-21 01:48:34 +1100526 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
Paul Mackerras0a730ae2006-10-03 21:32:49 +1000528 return cmd != 'X' && cmd != EOF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529}
530
531int xmon(struct pt_regs *excp)
532{
533 struct pt_regs regs;
534
535 if (excp == NULL) {
Anton Vorontsov322b4392008-12-17 10:08:55 +0000536 ppc_save_regs(&regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 excp = &regs;
538 }
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200539
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 return xmon_core(excp, 0);
541}
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000542EXPORT_SYMBOL(xmon);
543
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000544irqreturn_t xmon_irq(int irq, void *d)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000545{
546 unsigned long flags;
547 local_irq_save(flags);
548 printf("Keyboard interrupt\n");
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000549 xmon(get_irq_regs());
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000550 local_irq_restore(flags);
551 return IRQ_HANDLED;
552}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000554static int xmon_bpt(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
556 struct bpt *bp;
557 unsigned long offset;
558
559 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
560 return 0;
561
562 /* Are we at the trap at bp->instr[1] for some bp? */
563 bp = in_breakpoint_table(regs->nip, &offset);
564 if (bp != NULL && offset == 4) {
565 regs->nip = bp->address + 4;
566 atomic_dec(&bp->ref_count);
567 return 1;
568 }
569
570 /* Are we at a breakpoint? */
571 bp = at_breakpoint(regs->nip);
572 if (!bp)
573 return 0;
574
575 xmon_core(regs, 0);
576
577 return 1;
578}
579
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000580static int xmon_sstep(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581{
582 if (user_mode(regs))
583 return 0;
584 xmon_core(regs, 0);
585 return 1;
586}
587
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000588static int xmon_dabr_match(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
590 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
591 return 0;
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000592 if (dabr.enabled == 0)
593 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 xmon_core(regs, 0);
595 return 1;
596}
597
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000598static int xmon_iabr_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;
Michael Ellerman9f1067c22008-05-08 14:27:16 +1000602 if (iabr == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 return 0;
604 xmon_core(regs, 0);
605 return 1;
606}
607
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000608static int xmon_ipi(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
610#ifdef CONFIG_SMP
611 if (in_xmon && !cpu_isset(smp_processor_id(), cpus_in_xmon))
612 xmon_core(regs, 1);
613#endif
614 return 0;
615}
616
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000617static int xmon_fault_handler(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618{
619 struct bpt *bp;
620 unsigned long offset;
621
622 if (in_xmon && catch_memory_errors)
623 handle_fault(regs); /* doesn't return */
624
625 if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
626 bp = in_breakpoint_table(regs->nip, &offset);
627 if (bp != NULL) {
628 regs->nip = bp->address + offset;
629 atomic_dec(&bp->ref_count);
630 }
631 }
632
633 return 0;
634}
635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636static struct bpt *at_breakpoint(unsigned long pc)
637{
638 int i;
639 struct bpt *bp;
640
641 bp = bpts;
642 for (i = 0; i < NBPTS; ++i, ++bp)
643 if (bp->enabled && pc == bp->address)
644 return bp;
645 return NULL;
646}
647
648static struct bpt *in_breakpoint_table(unsigned long nip, unsigned long *offp)
649{
650 unsigned long off;
651
652 off = nip - (unsigned long) bpts;
653 if (off >= sizeof(bpts))
654 return NULL;
655 off %= sizeof(struct bpt);
656 if (off != offsetof(struct bpt, instr[0])
657 && off != offsetof(struct bpt, instr[1]))
658 return NULL;
659 *offp = off - offsetof(struct bpt, instr[0]);
660 return (struct bpt *) (nip - off);
661}
662
663static struct bpt *new_breakpoint(unsigned long a)
664{
665 struct bpt *bp;
666
667 a &= ~3UL;
668 bp = at_breakpoint(a);
669 if (bp)
670 return bp;
671
672 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
673 if (!bp->enabled && atomic_read(&bp->ref_count) == 0) {
674 bp->address = a;
675 bp->instr[1] = bpinstr;
676 store_inst(&bp->instr[1]);
677 return bp;
678 }
679 }
680
681 printf("Sorry, no free breakpoints. Please clear one first.\n");
682 return NULL;
683}
684
685static void insert_bpts(void)
686{
687 int i;
688 struct bpt *bp;
689
690 bp = bpts;
691 for (i = 0; i < NBPTS; ++i, ++bp) {
692 if ((bp->enabled & (BP_TRAP|BP_IABR)) == 0)
693 continue;
694 if (mread(bp->address, &bp->instr[0], 4) != 4) {
695 printf("Couldn't read instruction at %lx, "
696 "disabling breakpoint there\n", bp->address);
697 bp->enabled = 0;
698 continue;
699 }
700 if (IS_MTMSRD(bp->instr[0]) || IS_RFID(bp->instr[0])) {
701 printf("Breakpoint at %lx is on an mtmsrd or rfid "
702 "instruction, disabling it\n", bp->address);
703 bp->enabled = 0;
704 continue;
705 }
706 store_inst(&bp->instr[0]);
707 if (bp->enabled & BP_IABR)
708 continue;
709 if (mwrite(bp->address, &bpinstr, 4) != 4) {
710 printf("Couldn't write instruction at %lx, "
711 "disabling breakpoint there\n", bp->address);
712 bp->enabled &= ~BP_TRAP;
713 continue;
714 }
715 store_inst((void *)bp->address);
716 }
717}
718
719static void insert_cpu_bpts(void)
720{
721 if (dabr.enabled)
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000722 set_dabr(dabr.address | (dabr.enabled & 7));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 if (iabr && cpu_has_feature(CPU_FTR_IABR))
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000724 mtspr(SPRN_IABR, iabr->address
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 | (iabr->enabled & (BP_IABR|BP_IABR_TE)));
726}
727
728static void remove_bpts(void)
729{
730 int i;
731 struct bpt *bp;
732 unsigned instr;
733
734 bp = bpts;
735 for (i = 0; i < NBPTS; ++i, ++bp) {
736 if ((bp->enabled & (BP_TRAP|BP_IABR)) != BP_TRAP)
737 continue;
738 if (mread(bp->address, &instr, 4) == 4
739 && instr == bpinstr
740 && mwrite(bp->address, &bp->instr, 4) != 4)
741 printf("Couldn't remove breakpoint at %lx\n",
742 bp->address);
743 else
744 store_inst((void *)bp->address);
745 }
746}
747
748static void remove_cpu_bpts(void)
749{
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000750 set_dabr(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 if (cpu_has_feature(CPU_FTR_IABR))
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000752 mtspr(SPRN_IABR, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753}
754
755/* Command interpreting routine */
756static char *last_cmd;
757
758static int
759cmds(struct pt_regs *excp)
760{
761 int cmd = 0;
762
763 last_cmd = NULL;
764 xmon_regs = excp;
Olaf Hering26c8af52006-09-08 16:29:21 +0200765
766 if (!xmon_no_auto_backtrace) {
767 xmon_no_auto_backtrace = 1;
768 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
769 }
770
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 for(;;) {
772#ifdef CONFIG_SMP
773 printf("%x:", smp_processor_id());
774#endif /* CONFIG_SMP */
775 printf("mon> ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 flush_input();
777 termch = 0;
778 cmd = skipbl();
779 if( cmd == '\n' ) {
780 if (last_cmd == NULL)
781 continue;
782 take_input(last_cmd);
783 last_cmd = NULL;
784 cmd = inchar();
785 }
786 switch (cmd) {
787 case 'm':
788 cmd = inchar();
789 switch (cmd) {
790 case 'm':
791 case 's':
792 case 'd':
793 memops(cmd);
794 break;
795 case 'l':
796 memlocate();
797 break;
798 case 'z':
799 memzcan();
800 break;
801 case 'i':
802 show_mem();
803 break;
804 default:
805 termch = cmd;
806 memex();
807 }
808 break;
809 case 'd':
810 dump();
811 break;
812 case 'l':
813 symbol_lookup();
814 break;
815 case 'r':
816 prregs(excp); /* print regs */
817 break;
818 case 'e':
819 excprint(excp);
820 break;
821 case 'S':
822 super_regs();
823 break;
824 case 't':
825 backtrace(excp);
826 break;
827 case 'f':
828 cacheflush();
829 break;
830 case 's':
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200831 if (do_spu_cmd() == 0)
832 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 if (do_step(excp))
834 return cmd;
835 break;
836 case 'x':
837 case 'X':
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100838 return cmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 case EOF:
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100840 printf(" <no input ...>\n");
841 mdelay(2000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 return cmd;
843 case '?':
Ishizaki Kou4d404ed2007-07-18 19:26:40 +1000844 xmon_puts(help_string);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 case 'b':
847 bpt_cmds();
848 break;
849 case 'C':
850 csum();
851 break;
852 case 'c':
853 if (cpu_cmd())
854 return 0;
855 break;
856 case 'z':
857 bootcmds();
858 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000859 case 'p':
860 proccall();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000862#ifdef CONFIG_PPC_STD_MMU
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 case 'u':
864 dump_segments();
865 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000866#endif
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100867#ifdef CONFIG_4xx
868 case 'u':
869 dump_tlb_44x();
870 break;
871#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 default:
873 printf("Unrecognized command: ");
874 do {
875 if (' ' < cmd && cmd <= '~')
876 putchar(cmd);
877 else
878 printf("\\x%x", cmd);
879 cmd = inchar();
880 } while (cmd != '\n');
881 printf(" (type ? for help)\n");
882 break;
883 }
884 }
885}
886
887/*
888 * Step a single instruction.
889 * Some instructions we emulate, others we execute with MSR_SE set.
890 */
891static int do_step(struct pt_regs *regs)
892{
893 unsigned int instr;
894 int stepped;
895
896 /* check we are in 64-bit kernel mode, translation enabled */
897 if ((regs->msr & (MSR_SF|MSR_PR|MSR_IR)) == (MSR_SF|MSR_IR)) {
898 if (mread(regs->nip, &instr, 4) == 4) {
899 stepped = emulate_step(regs, instr);
900 if (stepped < 0) {
901 printf("Couldn't single-step %s instruction\n",
902 (IS_RFID(instr)? "rfid": "mtmsrd"));
903 return 0;
904 }
905 if (stepped > 0) {
906 regs->trap = 0xd00 | (regs->trap & 1);
907 printf("stepped to ");
908 xmon_print_symbol(regs->nip, " ", "\n");
909 ppc_inst_dump(regs->nip, 1, 0);
910 return 0;
911 }
912 }
913 }
914 regs->msr |= MSR_SE;
915 return 1;
916}
917
918static void bootcmds(void)
919{
920 int cmd;
921
922 cmd = inchar();
923 if (cmd == 'r')
924 ppc_md.restart(NULL);
925 else if (cmd == 'h')
926 ppc_md.halt();
927 else if (cmd == 'p')
928 ppc_md.power_off();
929}
930
931static int cpu_cmd(void)
932{
933#ifdef CONFIG_SMP
934 unsigned long cpu;
935 int timeout;
936 int count;
937
938 if (!scanhex(&cpu)) {
939 /* print cpus waiting or in xmon */
940 printf("cpus stopped:");
941 count = 0;
942 for (cpu = 0; cpu < NR_CPUS; ++cpu) {
943 if (cpu_isset(cpu, cpus_in_xmon)) {
944 if (count == 0)
945 printf(" %x", cpu);
946 ++count;
947 } else {
948 if (count > 1)
949 printf("-%x", cpu - 1);
950 count = 0;
951 }
952 }
953 if (count > 1)
954 printf("-%x", NR_CPUS - 1);
955 printf("\n");
956 return 0;
957 }
958 /* try to switch to cpu specified */
959 if (!cpu_isset(cpu, cpus_in_xmon)) {
960 printf("cpu 0x%x isn't in xmon\n", cpu);
961 return 0;
962 }
963 xmon_taken = 0;
964 mb();
965 xmon_owner = cpu;
966 timeout = 10000000;
967 while (!xmon_taken) {
968 if (--timeout == 0) {
969 if (test_and_set_bit(0, &xmon_taken))
970 break;
971 /* take control back */
972 mb();
973 xmon_owner = smp_processor_id();
974 printf("cpu %u didn't take control\n", cpu);
975 return 0;
976 }
977 barrier();
978 }
979 return 1;
980#else
981 return 0;
982#endif /* CONFIG_SMP */
983}
984
985static unsigned short fcstab[256] = {
986 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
987 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
988 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
989 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
990 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
991 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
992 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
993 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
994 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
995 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
996 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
997 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
998 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
999 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
1000 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
1001 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
1002 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
1003 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
1004 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
1005 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
1006 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
1007 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
1008 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
1009 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
1010 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
1011 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
1012 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
1013 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
1014 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
1015 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
1016 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
1017 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
1018};
1019
1020#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
1021
1022static void
1023csum(void)
1024{
1025 unsigned int i;
1026 unsigned short fcs;
1027 unsigned char v;
1028
1029 if (!scanhex(&adrs))
1030 return;
1031 if (!scanhex(&ncsum))
1032 return;
1033 fcs = 0xffff;
1034 for (i = 0; i < ncsum; ++i) {
1035 if (mread(adrs+i, &v, 1) == 0) {
1036 printf("csum stopped at %x\n", adrs+i);
1037 break;
1038 }
1039 fcs = FCS(fcs, v);
1040 }
1041 printf("%x\n", fcs);
1042}
1043
1044/*
1045 * Check if this is a suitable place to put a breakpoint.
1046 */
1047static long check_bp_loc(unsigned long addr)
1048{
1049 unsigned int instr;
1050
1051 addr &= ~3;
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001052 if (!is_kernel_addr(addr)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 printf("Breakpoints may only be placed at kernel addresses\n");
1054 return 0;
1055 }
1056 if (!mread(addr, &instr, sizeof(instr))) {
1057 printf("Can't read instruction at address %lx\n", addr);
1058 return 0;
1059 }
1060 if (IS_MTMSRD(instr) || IS_RFID(instr)) {
1061 printf("Breakpoints may not be placed on mtmsrd or rfid "
1062 "instructions\n");
1063 return 0;
1064 }
1065 return 1;
1066}
1067
1068static char *breakpoint_help_string =
1069 "Breakpoint command usage:\n"
1070 "b show breakpoints\n"
1071 "b <addr> [cnt] set breakpoint at given instr addr\n"
1072 "bc clear all breakpoints\n"
1073 "bc <n/addr> clear breakpoint number n or at addr\n"
1074 "bi <addr> [cnt] set hardware instr breakpoint (POWER3/RS64 only)\n"
1075 "bd <addr> [cnt] set hardware data breakpoint\n"
1076 "";
1077
1078static void
1079bpt_cmds(void)
1080{
1081 int cmd;
1082 unsigned long a;
1083 int mode, i;
1084 struct bpt *bp;
1085 const char badaddr[] = "Only kernel addresses are permitted "
1086 "for breakpoints\n";
1087
1088 cmd = inchar();
1089 switch (cmd) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001090#ifndef CONFIG_8xx
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 case 'd': /* bd - hardware data breakpoint */
1092 mode = 7;
1093 cmd = inchar();
1094 if (cmd == 'r')
1095 mode = 5;
1096 else if (cmd == 'w')
1097 mode = 6;
1098 else
1099 termch = cmd;
1100 dabr.address = 0;
1101 dabr.enabled = 0;
1102 if (scanhex(&dabr.address)) {
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001103 if (!is_kernel_addr(dabr.address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 printf(badaddr);
1105 break;
1106 }
1107 dabr.address &= ~7;
1108 dabr.enabled = mode | BP_DABR;
1109 }
1110 break;
1111
1112 case 'i': /* bi - hardware instr breakpoint */
1113 if (!cpu_has_feature(CPU_FTR_IABR)) {
1114 printf("Hardware instruction breakpoint "
1115 "not supported on this cpu\n");
1116 break;
1117 }
1118 if (iabr) {
1119 iabr->enabled &= ~(BP_IABR | BP_IABR_TE);
1120 iabr = NULL;
1121 }
1122 if (!scanhex(&a))
1123 break;
1124 if (!check_bp_loc(a))
1125 break;
1126 bp = new_breakpoint(a);
1127 if (bp != NULL) {
1128 bp->enabled |= BP_IABR | BP_IABR_TE;
1129 iabr = bp;
1130 }
1131 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001132#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133
1134 case 'c':
1135 if (!scanhex(&a)) {
1136 /* clear all breakpoints */
1137 for (i = 0; i < NBPTS; ++i)
1138 bpts[i].enabled = 0;
1139 iabr = NULL;
1140 dabr.enabled = 0;
1141 printf("All breakpoints cleared\n");
1142 break;
1143 }
1144
1145 if (a <= NBPTS && a >= 1) {
1146 /* assume a breakpoint number */
1147 bp = &bpts[a-1]; /* bp nums are 1 based */
1148 } else {
1149 /* assume a breakpoint address */
1150 bp = at_breakpoint(a);
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001151 if (bp == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 printf("No breakpoint at %x\n", a);
1153 break;
1154 }
1155 }
1156
1157 printf("Cleared breakpoint %x (", BP_NUM(bp));
1158 xmon_print_symbol(bp->address, " ", ")\n");
1159 bp->enabled = 0;
1160 break;
1161
1162 default:
1163 termch = cmd;
1164 cmd = skipbl();
1165 if (cmd == '?') {
1166 printf(breakpoint_help_string);
1167 break;
1168 }
1169 termch = cmd;
1170 if (!scanhex(&a)) {
1171 /* print all breakpoints */
1172 printf(" type address\n");
1173 if (dabr.enabled) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001174 printf(" data "REG" [", dabr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 if (dabr.enabled & 1)
1176 printf("r");
1177 if (dabr.enabled & 2)
1178 printf("w");
1179 printf("]\n");
1180 }
1181 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
1182 if (!bp->enabled)
1183 continue;
1184 printf("%2x %s ", BP_NUM(bp),
1185 (bp->enabled & BP_IABR)? "inst": "trap");
1186 xmon_print_symbol(bp->address, " ", "\n");
1187 }
1188 break;
1189 }
1190
1191 if (!check_bp_loc(a))
1192 break;
1193 bp = new_breakpoint(a);
1194 if (bp != NULL)
1195 bp->enabled |= BP_TRAP;
1196 break;
1197 }
1198}
1199
1200/* Very cheap human name for vector lookup. */
1201static
1202const char *getvecname(unsigned long vec)
1203{
1204 char *ret;
1205
1206 switch (vec) {
1207 case 0x100: ret = "(System Reset)"; break;
1208 case 0x200: ret = "(Machine Check)"; break;
1209 case 0x300: ret = "(Data Access)"; break;
1210 case 0x380: ret = "(Data SLB Access)"; break;
1211 case 0x400: ret = "(Instruction Access)"; break;
1212 case 0x480: ret = "(Instruction SLB Access)"; break;
1213 case 0x500: ret = "(Hardware Interrupt)"; break;
1214 case 0x600: ret = "(Alignment)"; break;
1215 case 0x700: ret = "(Program Check)"; break;
1216 case 0x800: ret = "(FPU Unavailable)"; break;
1217 case 0x900: ret = "(Decrementer)"; break;
1218 case 0xc00: ret = "(System Call)"; break;
1219 case 0xd00: ret = "(Single Step)"; break;
1220 case 0xf00: ret = "(Performance Monitor)"; break;
1221 case 0xf20: ret = "(Altivec Unavailable)"; break;
1222 case 0x1300: ret = "(Instruction Breakpoint)"; break;
1223 default: ret = "";
1224 }
1225 return ret;
1226}
1227
1228static void get_function_bounds(unsigned long pc, unsigned long *startp,
1229 unsigned long *endp)
1230{
1231 unsigned long size, offset;
1232 const char *name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233
1234 *startp = *endp = 0;
1235 if (pc == 0)
1236 return;
1237 if (setjmp(bus_error_jmp) == 0) {
1238 catch_memory_errors = 1;
1239 sync();
Alexey Dobriyanffb45122007-05-08 00:28:41 -07001240 name = kallsyms_lookup(pc, &size, &offset, NULL, tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 if (name != NULL) {
1242 *startp = pc - offset;
1243 *endp = pc - offset + size;
1244 }
1245 sync();
1246 }
1247 catch_memory_errors = 0;
1248}
1249
1250static int xmon_depth_to_print = 64;
1251
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001252#define LRSAVE_OFFSET (STACK_FRAME_LR_SAVE * sizeof(unsigned long))
1253#define MARKER_OFFSET (STACK_FRAME_MARKER * sizeof(unsigned long))
1254
1255#ifdef __powerpc64__
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001256#define REGS_OFFSET 0x70
1257#else
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001258#define REGS_OFFSET 16
1259#endif
1260
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261static void xmon_show_stack(unsigned long sp, unsigned long lr,
1262 unsigned long pc)
1263{
1264 unsigned long ip;
1265 unsigned long newsp;
1266 unsigned long marker;
1267 int count = 0;
1268 struct pt_regs regs;
1269
1270 do {
1271 if (sp < PAGE_OFFSET) {
1272 if (sp != 0)
1273 printf("SP (%lx) is in userspace\n", sp);
1274 break;
1275 }
1276
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001277 if (!mread(sp + LRSAVE_OFFSET, &ip, sizeof(unsigned long))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 || !mread(sp, &newsp, sizeof(unsigned long))) {
1279 printf("Couldn't read stack frame at %lx\n", sp);
1280 break;
1281 }
1282
1283 /*
1284 * For the first stack frame, try to work out if
1285 * LR and/or the saved LR value in the bottommost
1286 * stack frame are valid.
1287 */
1288 if ((pc | lr) != 0) {
1289 unsigned long fnstart, fnend;
1290 unsigned long nextip;
1291 int printip = 1;
1292
1293 get_function_bounds(pc, &fnstart, &fnend);
1294 nextip = 0;
1295 if (newsp > sp)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001296 mread(newsp + LRSAVE_OFFSET, &nextip,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 sizeof(unsigned long));
1298 if (lr == ip) {
1299 if (lr < PAGE_OFFSET
1300 || (fnstart <= lr && lr < fnend))
1301 printip = 0;
1302 } else if (lr == nextip) {
1303 printip = 0;
1304 } else if (lr >= PAGE_OFFSET
1305 && !(fnstart <= lr && lr < fnend)) {
1306 printf("[link register ] ");
1307 xmon_print_symbol(lr, " ", "\n");
1308 }
1309 if (printip) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001310 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 xmon_print_symbol(ip, " ", " (unreliable)\n");
1312 }
1313 pc = lr = 0;
1314
1315 } else {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001316 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 xmon_print_symbol(ip, " ", "\n");
1318 }
1319
1320 /* Look for "regshere" marker to see if this is
1321 an exception frame. */
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001322 if (mread(sp + MARKER_OFFSET, &marker, sizeof(unsigned long))
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001323 && marker == STACK_FRAME_REGS_MARKER) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001324 if (mread(sp + REGS_OFFSET, &regs, sizeof(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 != sizeof(regs)) {
1326 printf("Couldn't read registers at %lx\n",
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001327 sp + REGS_OFFSET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 break;
1329 }
1330 printf("--- Exception: %lx %s at ", regs.trap,
1331 getvecname(TRAP(&regs)));
1332 pc = regs.nip;
1333 lr = regs.link;
1334 xmon_print_symbol(pc, " ", "\n");
1335 }
1336
1337 if (newsp == 0)
1338 break;
1339
1340 sp = newsp;
1341 } while (count++ < xmon_depth_to_print);
1342}
1343
1344static void backtrace(struct pt_regs *excp)
1345{
1346 unsigned long sp;
1347
1348 if (scanhex(&sp))
1349 xmon_show_stack(sp, 0, 0);
1350 else
1351 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
1352 scannl();
1353}
1354
1355static void print_bug_trap(struct pt_regs *regs)
1356{
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001357#ifdef CONFIG_BUG
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001358 const struct bug_entry *bug;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 unsigned long addr;
1360
1361 if (regs->msr & MSR_PR)
1362 return; /* not in kernel */
1363 addr = regs->nip; /* address of trap instruction */
1364 if (addr < PAGE_OFFSET)
1365 return;
1366 bug = find_bug(regs->nip);
1367 if (bug == NULL)
1368 return;
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001369 if (is_warning_bug(bug))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 return;
1371
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001372#ifdef CONFIG_DEBUG_BUGVERBOSE
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001373 printf("kernel BUG at %s:%u!\n",
1374 bug->file, bug->line);
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001375#else
1376 printf("kernel BUG at %p!\n", (void *)bug->bug_addr);
1377#endif
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001378#endif /* CONFIG_BUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379}
1380
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001381static void excprint(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382{
1383 unsigned long trap;
1384
1385#ifdef CONFIG_SMP
1386 printf("cpu 0x%x: ", smp_processor_id());
1387#endif /* CONFIG_SMP */
1388
1389 trap = TRAP(fp);
1390 printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(trap), fp);
1391 printf(" pc: ");
1392 xmon_print_symbol(fp->nip, ": ", "\n");
1393
1394 printf(" lr: ", fp->link);
1395 xmon_print_symbol(fp->link, ": ", "\n");
1396
1397 printf(" sp: %lx\n", fp->gpr[1]);
1398 printf(" msr: %lx\n", fp->msr);
1399
1400 if (trap == 0x300 || trap == 0x380 || trap == 0x600) {
1401 printf(" dar: %lx\n", fp->dar);
1402 if (trap != 0x380)
1403 printf(" dsisr: %lx\n", fp->dsisr);
1404 }
1405
1406 printf(" current = 0x%lx\n", current);
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001407#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 printf(" paca = 0x%lx\n", get_paca());
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001409#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 if (current) {
1411 printf(" pid = %ld, comm = %s\n",
1412 current->pid, current->comm);
1413 }
1414
1415 if (trap == 0x700)
1416 print_bug_trap(fp);
1417}
1418
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001419static void prregs(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420{
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001421 int n, trap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 unsigned long base;
1423 struct pt_regs regs;
1424
1425 if (scanhex(&base)) {
1426 if (setjmp(bus_error_jmp) == 0) {
1427 catch_memory_errors = 1;
1428 sync();
1429 regs = *(struct pt_regs *)base;
1430 sync();
1431 __delay(200);
1432 } else {
1433 catch_memory_errors = 0;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001434 printf("*** Error reading registers from "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 base);
1436 return;
1437 }
1438 catch_memory_errors = 0;
1439 fp = &regs;
1440 }
1441
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001442#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 if (FULL_REGS(fp)) {
1444 for (n = 0; n < 16; ++n)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001445 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 n, fp->gpr[n], n+16, fp->gpr[n+16]);
1447 } else {
1448 for (n = 0; n < 7; ++n)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001449 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 n, fp->gpr[n], n+7, fp->gpr[n+7]);
1451 }
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001452#else
1453 for (n = 0; n < 32; ++n) {
1454 printf("R%.2d = %.8x%s", n, fp->gpr[n],
1455 (n & 3) == 3? "\n": " ");
1456 if (n == 12 && !FULL_REGS(fp)) {
1457 printf("\n");
1458 break;
1459 }
1460 }
1461#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 printf("pc = ");
1463 xmon_print_symbol(fp->nip, " ", "\n");
1464 printf("lr = ");
1465 xmon_print_symbol(fp->link, " ", "\n");
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001466 printf("msr = "REG" cr = %.8lx\n", fp->msr, fp->ccr);
1467 printf("ctr = "REG" xer = "REG" trap = %4lx\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 fp->ctr, fp->xer, fp->trap);
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001469 trap = TRAP(fp);
1470 if (trap == 0x300 || trap == 0x380 || trap == 0x600)
1471 printf("dar = "REG" dsisr = %.8lx\n", fp->dar, fp->dsisr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472}
1473
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001474static void cacheflush(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475{
1476 int cmd;
1477 unsigned long nflush;
1478
1479 cmd = inchar();
1480 if (cmd != 'i')
1481 termch = cmd;
1482 scanhex((void *)&adrs);
1483 if (termch != '\n')
1484 termch = 0;
1485 nflush = 1;
1486 scanhex(&nflush);
1487 nflush = (nflush + L1_CACHE_BYTES - 1) / L1_CACHE_BYTES;
1488 if (setjmp(bus_error_jmp) == 0) {
1489 catch_memory_errors = 1;
1490 sync();
1491
1492 if (cmd != 'i') {
1493 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1494 cflush((void *) adrs);
1495 } else {
1496 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1497 cinval((void *) adrs);
1498 }
1499 sync();
1500 /* wait a little while to see if we get a machine check */
1501 __delay(200);
1502 }
1503 catch_memory_errors = 0;
1504}
1505
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001506static unsigned long
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507read_spr(int n)
1508{
1509 unsigned int instrs[2];
1510 unsigned long (*code)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 unsigned long ret = -1UL;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001512#ifdef CONFIG_PPC64
1513 unsigned long opd[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 opd[0] = (unsigned long)instrs;
1516 opd[1] = 0;
1517 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001518 code = (unsigned long (*)(void)) opd;
1519#else
1520 code = (unsigned long (*)(void)) instrs;
1521#endif
1522
1523 /* mfspr r3,n; blr */
1524 instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1525 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 store_inst(instrs);
1527 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
1529 if (setjmp(bus_error_jmp) == 0) {
1530 catch_memory_errors = 1;
1531 sync();
1532
1533 ret = code();
1534
1535 sync();
1536 /* wait a little while to see if we get a machine check */
1537 __delay(200);
1538 n = size;
1539 }
1540
1541 return ret;
1542}
1543
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001544static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545write_spr(int n, unsigned long val)
1546{
1547 unsigned int instrs[2];
1548 unsigned long (*code)(unsigned long);
Paul Mackerras548cceb2005-11-11 22:36:34 +11001549#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 unsigned long opd[3];
1551
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 opd[0] = (unsigned long)instrs;
1553 opd[1] = 0;
1554 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001555 code = (unsigned long (*)(unsigned long)) opd;
1556#else
1557 code = (unsigned long (*)(unsigned long)) instrs;
1558#endif
1559
1560 instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1561 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 store_inst(instrs);
1563 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564
1565 if (setjmp(bus_error_jmp) == 0) {
1566 catch_memory_errors = 1;
1567 sync();
1568
1569 code(val);
1570
1571 sync();
1572 /* wait a little while to see if we get a machine check */
1573 __delay(200);
1574 n = size;
1575 }
1576}
1577
1578static unsigned long regno;
1579extern char exc_prolog;
1580extern char dec_exc;
1581
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001582static void super_regs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583{
1584 int cmd;
1585 unsigned long val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
1587 cmd = skipbl();
1588 if (cmd == '\n') {
1589 unsigned long sp, toc;
1590 asm("mr %0,1" : "=r" (sp) :);
1591 asm("mr %0,2" : "=r" (toc) :);
1592
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001593 printf("msr = "REG" sprg0= "REG"\n",
1594 mfmsr(), mfspr(SPRN_SPRG0));
1595 printf("pvr = "REG" sprg1= "REG"\n",
1596 mfspr(SPRN_PVR), mfspr(SPRN_SPRG1));
1597 printf("dec = "REG" sprg2= "REG"\n",
1598 mfspr(SPRN_DEC), mfspr(SPRN_SPRG2));
1599 printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3));
1600 printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601#ifdef CONFIG_PPC_ISERIES
Stephen Rothwell1d135812006-11-13 14:50:28 +11001602 if (firmware_has_feature(FW_FEATURE_ISERIES)) {
1603 struct paca_struct *ptrPaca;
1604 struct lppaca *ptrLpPaca;
Stephen Rothwell1d135812006-11-13 14:50:28 +11001605
1606 /* Dump out relevant Paca data areas. */
1607 printf("Paca: \n");
1608 ptrPaca = get_paca();
1609
1610 printf(" Local Processor Control Area (LpPaca): \n");
1611 ptrLpPaca = ptrPaca->lppaca_ptr;
1612 printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n",
1613 ptrLpPaca->saved_srr0, ptrLpPaca->saved_srr1);
1614 printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n",
1615 ptrLpPaca->saved_gpr3, ptrLpPaca->saved_gpr4);
1616 printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->saved_gpr5);
Stephen Rothwell1d135812006-11-13 14:50:28 +11001617 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618#endif
1619
1620 return;
1621 }
1622
1623 scanhex(&regno);
1624 switch (cmd) {
1625 case 'w':
1626 val = read_spr(regno);
1627 scanhex(&val);
1628 write_spr(regno, val);
1629 /* fall through */
1630 case 'r':
1631 printf("spr %lx = %lx\n", regno, read_spr(regno));
1632 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 }
1634 scannl();
1635}
1636
1637/*
1638 * Stuff for reading and writing memory safely
1639 */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001640static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641mread(unsigned long adrs, void *buf, int size)
1642{
1643 volatile int n;
1644 char *p, *q;
1645
1646 n = 0;
1647 if (setjmp(bus_error_jmp) == 0) {
1648 catch_memory_errors = 1;
1649 sync();
1650 p = (char *)adrs;
1651 q = (char *)buf;
1652 switch (size) {
1653 case 2:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001654 *(u16 *)q = *(u16 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 break;
1656 case 4:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001657 *(u32 *)q = *(u32 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658 break;
1659 case 8:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001660 *(u64 *)q = *(u64 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 break;
1662 default:
1663 for( ; n < size; ++n) {
1664 *q++ = *p++;
1665 sync();
1666 }
1667 }
1668 sync();
1669 /* wait a little while to see if we get a machine check */
1670 __delay(200);
1671 n = size;
1672 }
1673 catch_memory_errors = 0;
1674 return n;
1675}
1676
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001677static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678mwrite(unsigned long adrs, void *buf, int size)
1679{
1680 volatile int n;
1681 char *p, *q;
1682
1683 n = 0;
1684 if (setjmp(bus_error_jmp) == 0) {
1685 catch_memory_errors = 1;
1686 sync();
1687 p = (char *) adrs;
1688 q = (char *) buf;
1689 switch (size) {
1690 case 2:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001691 *(u16 *)p = *(u16 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 break;
1693 case 4:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001694 *(u32 *)p = *(u32 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 break;
1696 case 8:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001697 *(u64 *)p = *(u64 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 break;
1699 default:
1700 for ( ; n < size; ++n) {
1701 *p++ = *q++;
1702 sync();
1703 }
1704 }
1705 sync();
1706 /* wait a little while to see if we get a machine check */
1707 __delay(200);
1708 n = size;
1709 } else {
1710 printf("*** Error writing address %x\n", adrs + n);
1711 }
1712 catch_memory_errors = 0;
1713 return n;
1714}
1715
1716static int fault_type;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001717static int fault_except;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718static char *fault_chars[] = { "--", "**", "##" };
1719
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001720static int handle_fault(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721{
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001722 fault_except = TRAP(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723 switch (TRAP(regs)) {
1724 case 0x200:
1725 fault_type = 0;
1726 break;
1727 case 0x300:
1728 case 0x380:
1729 fault_type = 1;
1730 break;
1731 default:
1732 fault_type = 2;
1733 }
1734
1735 longjmp(bus_error_jmp, 1);
1736
1737 return 0;
1738}
1739
1740#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
1741
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001742static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743byterev(unsigned char *val, int size)
1744{
1745 int t;
1746
1747 switch (size) {
1748 case 2:
1749 SWAP(val[0], val[1], t);
1750 break;
1751 case 4:
1752 SWAP(val[0], val[3], t);
1753 SWAP(val[1], val[2], t);
1754 break;
1755 case 8: /* is there really any use for this? */
1756 SWAP(val[0], val[7], t);
1757 SWAP(val[1], val[6], t);
1758 SWAP(val[2], val[5], t);
1759 SWAP(val[3], val[4], t);
1760 break;
1761 }
1762}
1763
1764static int brev;
1765static int mnoread;
1766
1767static char *memex_help_string =
1768 "Memory examine command usage:\n"
1769 "m [addr] [flags] examine/change memory\n"
1770 " addr is optional. will start where left off.\n"
1771 " flags may include chars from this set:\n"
1772 " b modify by bytes (default)\n"
1773 " w modify by words (2 byte)\n"
1774 " l modify by longs (4 byte)\n"
1775 " d modify by doubleword (8 byte)\n"
1776 " r toggle reverse byte order mode\n"
1777 " n do not read memory (for i/o spaces)\n"
1778 " . ok to read (default)\n"
1779 "NOTE: flags are saved as defaults\n"
1780 "";
1781
1782static char *memex_subcmd_help_string =
1783 "Memory examine subcommands:\n"
1784 " hexval write this val to current location\n"
1785 " 'string' write chars from string to this location\n"
1786 " ' increment address\n"
1787 " ^ decrement address\n"
1788 " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n"
1789 " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n"
1790 " ` clear no-read flag\n"
1791 " ; stay at this addr\n"
1792 " v change to byte mode\n"
1793 " w change to word (2 byte) mode\n"
1794 " l change to long (4 byte) mode\n"
1795 " u change to doubleword (8 byte) mode\n"
1796 " m addr change current addr\n"
1797 " n toggle no-read flag\n"
1798 " r toggle byte reverse flag\n"
1799 " < count back up count bytes\n"
1800 " > count skip forward count bytes\n"
1801 " x exit this mode\n"
1802 "";
1803
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001804static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805memex(void)
1806{
1807 int cmd, inc, i, nslash;
1808 unsigned long n;
1809 unsigned char val[16];
1810
1811 scanhex((void *)&adrs);
1812 cmd = skipbl();
1813 if (cmd == '?') {
1814 printf(memex_help_string);
1815 return;
1816 } else {
1817 termch = cmd;
1818 }
1819 last_cmd = "m\n";
1820 while ((cmd = skipbl()) != '\n') {
1821 switch( cmd ){
1822 case 'b': size = 1; break;
1823 case 'w': size = 2; break;
1824 case 'l': size = 4; break;
1825 case 'd': size = 8; break;
1826 case 'r': brev = !brev; break;
1827 case 'n': mnoread = 1; break;
1828 case '.': mnoread = 0; break;
1829 }
1830 }
1831 if( size <= 0 )
1832 size = 1;
1833 else if( size > 8 )
1834 size = 8;
1835 for(;;){
1836 if (!mnoread)
1837 n = mread(adrs, val, size);
Paul Mackerrase1449ed2005-11-10 14:30:20 +11001838 printf(REG"%c", adrs, brev? 'r': ' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839 if (!mnoread) {
1840 if (brev)
1841 byterev(val, size);
1842 putchar(' ');
1843 for (i = 0; i < n; ++i)
1844 printf("%.2x", val[i]);
1845 for (; i < size; ++i)
1846 printf("%s", fault_chars[fault_type]);
1847 }
1848 putchar(' ');
1849 inc = size;
1850 nslash = 0;
1851 for(;;){
1852 if( scanhex(&n) ){
1853 for (i = 0; i < size; ++i)
1854 val[i] = n >> (i * 8);
1855 if (!brev)
1856 byterev(val, size);
1857 mwrite(adrs, val, size);
1858 inc = size;
1859 }
1860 cmd = skipbl();
1861 if (cmd == '\n')
1862 break;
1863 inc = 0;
1864 switch (cmd) {
1865 case '\'':
1866 for(;;){
1867 n = inchar();
1868 if( n == '\\' )
1869 n = bsesc();
1870 else if( n == '\'' )
1871 break;
1872 for (i = 0; i < size; ++i)
1873 val[i] = n >> (i * 8);
1874 if (!brev)
1875 byterev(val, size);
1876 mwrite(adrs, val, size);
1877 adrs += size;
1878 }
1879 adrs -= size;
1880 inc = size;
1881 break;
1882 case ',':
1883 adrs += size;
1884 break;
1885 case '.':
1886 mnoread = 0;
1887 break;
1888 case ';':
1889 break;
1890 case 'x':
1891 case EOF:
1892 scannl();
1893 return;
1894 case 'b':
1895 case 'v':
1896 size = 1;
1897 break;
1898 case 'w':
1899 size = 2;
1900 break;
1901 case 'l':
1902 size = 4;
1903 break;
1904 case 'u':
1905 size = 8;
1906 break;
1907 case '^':
1908 adrs -= size;
1909 break;
1910 break;
1911 case '/':
1912 if (nslash > 0)
1913 adrs -= 1 << nslash;
1914 else
1915 nslash = 0;
1916 nslash += 4;
1917 adrs += 1 << nslash;
1918 break;
1919 case '\\':
1920 if (nslash < 0)
1921 adrs += 1 << -nslash;
1922 else
1923 nslash = 0;
1924 nslash -= 4;
1925 adrs -= 1 << -nslash;
1926 break;
1927 case 'm':
1928 scanhex((void *)&adrs);
1929 break;
1930 case 'n':
1931 mnoread = 1;
1932 break;
1933 case 'r':
1934 brev = !brev;
1935 break;
1936 case '<':
1937 n = size;
1938 scanhex(&n);
1939 adrs -= n;
1940 break;
1941 case '>':
1942 n = size;
1943 scanhex(&n);
1944 adrs += n;
1945 break;
1946 case '?':
1947 printf(memex_subcmd_help_string);
1948 break;
1949 }
1950 }
1951 adrs += inc;
1952 }
1953}
1954
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001955static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956bsesc(void)
1957{
1958 int c;
1959
1960 c = inchar();
1961 switch( c ){
1962 case 'n': c = '\n'; break;
1963 case 'r': c = '\r'; break;
1964 case 'b': c = '\b'; break;
1965 case 't': c = '\t'; break;
1966 }
1967 return c;
1968}
1969
Olaf Hering7e5b5932006-03-08 20:40:28 +01001970static void xmon_rawdump (unsigned long adrs, long ndump)
1971{
1972 long n, m, r, nr;
1973 unsigned char temp[16];
1974
1975 for (n = ndump; n > 0;) {
1976 r = n < 16? n: 16;
1977 nr = mread(adrs, temp, r);
1978 adrs += nr;
1979 for (m = 0; m < r; ++m) {
1980 if (m < nr)
1981 printf("%.2x", temp[m]);
1982 else
1983 printf("%s", fault_chars[fault_type]);
1984 }
1985 n -= r;
1986 if (nr < r)
1987 break;
1988 }
1989 printf("\n");
1990}
1991
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992#define isxdigit(c) (('0' <= (c) && (c) <= '9') \
1993 || ('a' <= (c) && (c) <= 'f') \
1994 || ('A' <= (c) && (c) <= 'F'))
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001995static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996dump(void)
1997{
1998 int c;
1999
2000 c = inchar();
2001 if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n')
2002 termch = c;
2003 scanhex((void *)&adrs);
2004 if (termch != '\n')
2005 termch = 0;
2006 if (c == 'i') {
2007 scanhex(&nidump);
2008 if (nidump == 0)
2009 nidump = 16;
2010 else if (nidump > MAX_DUMP)
2011 nidump = MAX_DUMP;
2012 adrs += ppc_inst_dump(adrs, nidump, 1);
2013 last_cmd = "di\n";
Vinay Sridharf312deb2009-05-14 23:13:07 +00002014 } else if (c == 'l') {
2015 dump_log_buf();
Olaf Hering7e5b5932006-03-08 20:40:28 +01002016 } else if (c == 'r') {
2017 scanhex(&ndump);
2018 if (ndump == 0)
2019 ndump = 64;
2020 xmon_rawdump(adrs, ndump);
2021 adrs += ndump;
2022 last_cmd = "dr\n";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 } else {
2024 scanhex(&ndump);
2025 if (ndump == 0)
2026 ndump = 64;
2027 else if (ndump > MAX_DUMP)
2028 ndump = MAX_DUMP;
2029 prdump(adrs, ndump);
2030 adrs += ndump;
2031 last_cmd = "d\n";
2032 }
2033}
2034
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002035static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036prdump(unsigned long adrs, long ndump)
2037{
2038 long n, m, c, r, nr;
2039 unsigned char temp[16];
2040
2041 for (n = ndump; n > 0;) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002042 printf(REG, adrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 putchar(' ');
2044 r = n < 16? n: 16;
2045 nr = mread(adrs, temp, r);
2046 adrs += nr;
2047 for (m = 0; m < r; ++m) {
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002048 if ((m & (sizeof(long) - 1)) == 0 && m > 0)
2049 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 if (m < nr)
2051 printf("%.2x", temp[m]);
2052 else
2053 printf("%s", fault_chars[fault_type]);
2054 }
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002055 for (; m < 16; ++m) {
2056 if ((m & (sizeof(long) - 1)) == 0)
2057 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 printf(" ");
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002059 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 printf(" |");
2061 for (m = 0; m < r; ++m) {
2062 if (m < nr) {
2063 c = temp[m];
2064 putchar(' ' <= c && c <= '~'? c: '.');
2065 } else
2066 putchar(' ');
2067 }
2068 n -= r;
2069 for (; m < 16; ++m)
2070 putchar(' ');
2071 printf("|\n");
2072 if (nr < r)
2073 break;
2074 }
2075}
2076
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002077typedef int (*instruction_dump_func)(unsigned long inst, unsigned long addr);
2078
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002079static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002080generic_inst_dump(unsigned long adr, long count, int praddr,
2081 instruction_dump_func dump_func)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082{
2083 int nr, dotted;
2084 unsigned long first_adr;
2085 unsigned long inst, last_inst = 0;
2086 unsigned char val[4];
2087
2088 dotted = 0;
2089 for (first_adr = adr; count > 0; --count, adr += 4) {
2090 nr = mread(adr, val, 4);
2091 if (nr == 0) {
2092 if (praddr) {
2093 const char *x = fault_chars[fault_type];
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002094 printf(REG" %s%s%s%s\n", adr, x, x, x, x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 }
2096 break;
2097 }
2098 inst = GETWORD(val);
2099 if (adr > first_adr && inst == last_inst) {
2100 if (!dotted) {
2101 printf(" ...\n");
2102 dotted = 1;
2103 }
2104 continue;
2105 }
2106 dotted = 0;
2107 last_inst = inst;
2108 if (praddr)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002109 printf(REG" %.8x", adr, inst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 printf("\t");
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002111 dump_func(inst, adr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112 printf("\n");
2113 }
2114 return adr - first_adr;
2115}
2116
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002117static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002118ppc_inst_dump(unsigned long adr, long count, int praddr)
2119{
2120 return generic_inst_dump(adr, count, praddr, print_insn_powerpc);
2121}
2122
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123void
2124print_address(unsigned long addr)
2125{
2126 xmon_print_symbol(addr, "\t# ", "");
2127}
2128
Vinay Sridharf312deb2009-05-14 23:13:07 +00002129void
2130dump_log_buf(void)
2131{
2132 const unsigned long size = 128;
Stephen Rothwell6d1386d2009-06-02 18:15:33 +00002133 unsigned long end, addr;
Vinay Sridharf312deb2009-05-14 23:13:07 +00002134 unsigned char buf[size + 1];
2135
2136 addr = 0;
2137 buf[size] = '\0';
2138
2139 if (setjmp(bus_error_jmp) != 0) {
2140 printf("Unable to lookup symbol __log_buf!\n");
2141 return;
2142 }
2143
2144 catch_memory_errors = 1;
2145 sync();
2146 addr = kallsyms_lookup_name("__log_buf");
2147
2148 if (! addr)
2149 printf("Symbol __log_buf not found!\n");
2150 else {
2151 end = addr + (1 << CONFIG_LOG_BUF_SHIFT);
2152 while (addr < end) {
2153 if (! mread(addr, buf, size)) {
2154 printf("Can't read memory at address 0x%lx\n", addr);
2155 break;
2156 }
2157
2158 printf("%s", buf);
2159
2160 if (strlen(buf) < size)
2161 break;
2162
2163 addr += size;
2164 }
2165 }
2166
2167 sync();
2168 /* wait a little while to see if we get a machine check */
2169 __delay(200);
2170 catch_memory_errors = 0;
2171}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172
2173/*
2174 * Memory operations - move, set, print differences
2175 */
2176static unsigned long mdest; /* destination address */
2177static unsigned long msrc; /* source address */
2178static unsigned long mval; /* byte value to set memory to */
2179static unsigned long mcount; /* # bytes to affect */
2180static unsigned long mdiffs; /* max # differences to print */
2181
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002182static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183memops(int cmd)
2184{
2185 scanhex((void *)&mdest);
2186 if( termch != '\n' )
2187 termch = 0;
2188 scanhex((void *)(cmd == 's'? &mval: &msrc));
2189 if( termch != '\n' )
2190 termch = 0;
2191 scanhex((void *)&mcount);
2192 switch( cmd ){
2193 case 'm':
2194 memmove((void *)mdest, (void *)msrc, mcount);
2195 break;
2196 case 's':
2197 memset((void *)mdest, mval, mcount);
2198 break;
2199 case 'd':
2200 if( termch != '\n' )
2201 termch = 0;
2202 scanhex((void *)&mdiffs);
2203 memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs);
2204 break;
2205 }
2206}
2207
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002208static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr)
2210{
2211 unsigned n, prt;
2212
2213 prt = 0;
2214 for( n = nb; n > 0; --n )
2215 if( *p1++ != *p2++ )
2216 if( ++prt <= maxpr )
2217 printf("%.16x %.2x # %.16x %.2x\n", p1 - 1,
2218 p1[-1], p2 - 1, p2[-1]);
2219 if( prt > maxpr )
2220 printf("Total of %d differences\n", prt);
2221}
2222
2223static unsigned mend;
2224static unsigned mask;
2225
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002226static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227memlocate(void)
2228{
2229 unsigned a, n;
2230 unsigned char val[4];
2231
2232 last_cmd = "ml";
2233 scanhex((void *)&mdest);
2234 if (termch != '\n') {
2235 termch = 0;
2236 scanhex((void *)&mend);
2237 if (termch != '\n') {
2238 termch = 0;
2239 scanhex((void *)&mval);
2240 mask = ~0;
2241 if (termch != '\n') termch = 0;
2242 scanhex((void *)&mask);
2243 }
2244 }
2245 n = 0;
2246 for (a = mdest; a < mend; a += 4) {
2247 if (mread(a, val, 4) == 4
2248 && ((GETWORD(val) ^ mval) & mask) == 0) {
2249 printf("%.16x: %.16x\n", a, GETWORD(val));
2250 if (++n >= 10)
2251 break;
2252 }
2253 }
2254}
2255
2256static unsigned long mskip = 0x1000;
2257static unsigned long mlim = 0xffffffff;
2258
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002259static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260memzcan(void)
2261{
2262 unsigned char v;
2263 unsigned a;
2264 int ok, ook;
2265
2266 scanhex(&mdest);
2267 if (termch != '\n') termch = 0;
2268 scanhex(&mskip);
2269 if (termch != '\n') termch = 0;
2270 scanhex(&mlim);
2271 ook = 0;
2272 for (a = mdest; a < mlim; a += mskip) {
2273 ok = mread(a, &v, 1);
2274 if (ok && !ook) {
2275 printf("%.8x .. ", a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 } else if (!ok && ook)
2277 printf("%.8x\n", a - mskip);
2278 ook = ok;
2279 if (a + mskip < a)
2280 break;
2281 }
2282 if (ook)
2283 printf("%.8x\n", a - mskip);
2284}
2285
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002286static void proccall(void)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002287{
2288 unsigned long args[8];
2289 unsigned long ret;
2290 int i;
2291 typedef unsigned long (*callfunc_t)(unsigned long, unsigned long,
2292 unsigned long, unsigned long, unsigned long,
2293 unsigned long, unsigned long, unsigned long);
2294 callfunc_t func;
2295
2296 if (!scanhex(&adrs))
2297 return;
2298 if (termch != '\n')
2299 termch = 0;
2300 for (i = 0; i < 8; ++i)
2301 args[i] = 0;
2302 for (i = 0; i < 8; ++i) {
2303 if (!scanhex(&args[i]) || termch == '\n')
2304 break;
2305 termch = 0;
2306 }
2307 func = (callfunc_t) adrs;
2308 ret = 0;
2309 if (setjmp(bus_error_jmp) == 0) {
2310 catch_memory_errors = 1;
2311 sync();
2312 ret = func(args[0], args[1], args[2], args[3],
2313 args[4], args[5], args[6], args[7]);
2314 sync();
2315 printf("return value is %x\n", ret);
2316 } else {
2317 printf("*** %x exception occurred\n", fault_except);
2318 }
2319 catch_memory_errors = 0;
2320}
2321
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322/* Input scanning routines */
2323int
2324skipbl(void)
2325{
2326 int c;
2327
2328 if( termch != 0 ){
2329 c = termch;
2330 termch = 0;
2331 } else
2332 c = inchar();
2333 while( c == ' ' || c == '\t' )
2334 c = inchar();
2335 return c;
2336}
2337
2338#define N_PTREGS 44
2339static char *regnames[N_PTREGS] = {
2340 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
2341 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
2342 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
2343 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002344 "pc", "msr", "or3", "ctr", "lr", "xer", "ccr",
2345#ifdef CONFIG_PPC64
2346 "softe",
2347#else
2348 "mq",
2349#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350 "trap", "dar", "dsisr", "res"
2351};
2352
2353int
2354scanhex(unsigned long *vp)
2355{
2356 int c, d;
2357 unsigned long v;
2358
2359 c = skipbl();
2360 if (c == '%') {
2361 /* parse register name */
2362 char regname[8];
2363 int i;
2364
2365 for (i = 0; i < sizeof(regname) - 1; ++i) {
2366 c = inchar();
2367 if (!isalnum(c)) {
2368 termch = c;
2369 break;
2370 }
2371 regname[i] = c;
2372 }
2373 regname[i] = 0;
2374 for (i = 0; i < N_PTREGS; ++i) {
2375 if (strcmp(regnames[i], regname) == 0) {
2376 if (xmon_regs == NULL) {
2377 printf("regs not available\n");
2378 return 0;
2379 }
2380 *vp = ((unsigned long *)xmon_regs)[i];
2381 return 1;
2382 }
2383 }
2384 printf("invalid register name '%%%s'\n", regname);
2385 return 0;
2386 }
2387
2388 /* skip leading "0x" if any */
2389
2390 if (c == '0') {
2391 c = inchar();
2392 if (c == 'x') {
2393 c = inchar();
2394 } else {
2395 d = hexdigit(c);
2396 if (d == EOF) {
2397 termch = c;
2398 *vp = 0;
2399 return 1;
2400 }
2401 }
2402 } else if (c == '$') {
2403 int i;
2404 for (i=0; i<63; i++) {
2405 c = inchar();
2406 if (isspace(c)) {
2407 termch = c;
2408 break;
2409 }
2410 tmpstr[i] = c;
2411 }
2412 tmpstr[i++] = 0;
Benjamin Herrenschmidt6879dc12005-06-21 17:15:30 -07002413 *vp = 0;
2414 if (setjmp(bus_error_jmp) == 0) {
2415 catch_memory_errors = 1;
2416 sync();
2417 *vp = kallsyms_lookup_name(tmpstr);
2418 sync();
2419 }
2420 catch_memory_errors = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421 if (!(*vp)) {
2422 printf("unknown symbol '%s'\n", tmpstr);
2423 return 0;
2424 }
2425 return 1;
2426 }
2427
2428 d = hexdigit(c);
2429 if (d == EOF) {
2430 termch = c;
2431 return 0;
2432 }
2433 v = 0;
2434 do {
2435 v = (v << 4) + d;
2436 c = inchar();
2437 d = hexdigit(c);
2438 } while (d != EOF);
2439 termch = c;
2440 *vp = v;
2441 return 1;
2442}
2443
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002444static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445scannl(void)
2446{
2447 int c;
2448
2449 c = termch;
2450 termch = 0;
2451 while( c != '\n' )
2452 c = inchar();
2453}
2454
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002455static int hexdigit(int c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456{
2457 if( '0' <= c && c <= '9' )
2458 return c - '0';
2459 if( 'A' <= c && c <= 'F' )
2460 return c - ('A' - 10);
2461 if( 'a' <= c && c <= 'f' )
2462 return c - ('a' - 10);
2463 return EOF;
2464}
2465
2466void
2467getstring(char *s, int size)
2468{
2469 int c;
2470
2471 c = skipbl();
2472 do {
2473 if( size > 1 ){
2474 *s++ = c;
2475 --size;
2476 }
2477 c = inchar();
2478 } while( c != ' ' && c != '\t' && c != '\n' );
2479 termch = c;
2480 *s = 0;
2481}
2482
2483static char line[256];
2484static char *lineptr;
2485
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002486static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487flush_input(void)
2488{
2489 lineptr = NULL;
2490}
2491
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002492static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493inchar(void)
2494{
2495 if (lineptr == NULL || *lineptr == 0) {
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002496 if (xmon_gets(line, sizeof(line)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497 lineptr = NULL;
2498 return EOF;
2499 }
2500 lineptr = line;
2501 }
2502 return *lineptr++;
2503}
2504
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002505static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506take_input(char *str)
2507{
2508 lineptr = str;
2509}
2510
2511
2512static void
2513symbol_lookup(void)
2514{
2515 int type = inchar();
2516 unsigned long addr;
2517 static char tmp[64];
2518
2519 switch (type) {
2520 case 'a':
2521 if (scanhex(&addr))
2522 xmon_print_symbol(addr, ": ", "\n");
2523 termch = 0;
2524 break;
2525 case 's':
2526 getstring(tmp, 64);
2527 if (setjmp(bus_error_jmp) == 0) {
2528 catch_memory_errors = 1;
2529 sync();
2530 addr = kallsyms_lookup_name(tmp);
2531 if (addr)
2532 printf("%s: %lx\n", tmp, addr);
2533 else
2534 printf("Symbol '%s' not found.\n", tmp);
2535 sync();
2536 }
2537 catch_memory_errors = 0;
2538 termch = 0;
2539 break;
2540 }
2541}
2542
2543
2544/* Print an address in numeric and symbolic form (if possible) */
2545static void xmon_print_symbol(unsigned long address, const char *mid,
2546 const char *after)
2547{
2548 char *modname;
2549 const char *name = NULL;
2550 unsigned long offset, size;
2551
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002552 printf(REG, address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553 if (setjmp(bus_error_jmp) == 0) {
2554 catch_memory_errors = 1;
2555 sync();
2556 name = kallsyms_lookup(address, &size, &offset, &modname,
2557 tmpstr);
2558 sync();
2559 /* wait a little while to see if we get a machine check */
2560 __delay(200);
2561 }
2562
2563 catch_memory_errors = 0;
2564
2565 if (name) {
2566 printf("%s%s+%#lx/%#lx", mid, name, offset, size);
2567 if (modname)
2568 printf(" [%s]", modname);
2569 }
2570 printf("%s", after);
2571}
2572
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002573#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574static void dump_slb(void)
2575{
2576 int i;
will schmidtb3b95952007-12-07 08:22:23 +11002577 unsigned long esid,vsid,valid;
2578 unsigned long llp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579
2580 printf("SLB contents of cpu %x\n", smp_processor_id());
2581
Michael Neuling584f8b72007-12-06 17:24:48 +11002582 for (i = 0; i < mmu_slb_size; i++) {
will schmidtb3b95952007-12-07 08:22:23 +11002583 asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i));
2584 asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i));
2585 valid = (esid & SLB_ESID_V);
2586 if (valid | esid | vsid) {
2587 printf("%02d %016lx %016lx", i, esid, vsid);
2588 if (valid) {
2589 llp = vsid & SLB_VSID_LLP;
2590 if (vsid & SLB_VSID_B_1T) {
2591 printf(" 1T ESID=%9lx VSID=%13lx LLP:%3lx \n",
2592 GET_ESID_1T(esid),
2593 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT_1T,
2594 llp);
2595 } else {
2596 printf(" 256M ESID=%9lx VSID=%13lx LLP:%3lx \n",
2597 GET_ESID(esid),
2598 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT,
2599 llp);
2600 }
2601 } else
2602 printf("\n");
2603 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604 }
2605}
2606
2607static void dump_stab(void)
2608{
2609 int i;
2610 unsigned long *tmp = (unsigned long *)get_paca()->stab_addr;
2611
2612 printf("Segment table contents of cpu %x\n", smp_processor_id());
2613
2614 for (i = 0; i < PAGE_SIZE/16; i++) {
2615 unsigned long a, b;
2616
2617 a = *tmp++;
2618 b = *tmp++;
2619
2620 if (a || b) {
2621 printf("%03d %016lx ", i, a);
2622 printf("%016lx\n", b);
2623 }
2624 }
2625}
2626
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002627void dump_segments(void)
2628{
2629 if (cpu_has_feature(CPU_FTR_SLB))
2630 dump_slb();
2631 else
2632 dump_stab();
2633}
2634#endif
2635
2636#ifdef CONFIG_PPC_STD_MMU_32
2637void dump_segments(void)
2638{
2639 int i;
2640
2641 printf("sr0-15 =");
2642 for (i = 0; i < 16; ++i)
2643 printf(" %x", mfsrin(i));
2644 printf("\n");
2645}
2646#endif
2647
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +11002648#ifdef CONFIG_44x
2649static void dump_tlb_44x(void)
2650{
2651 int i;
2652
2653 for (i = 0; i < PPC44x_TLB_SIZE; i++) {
2654 unsigned long w0,w1,w2;
2655 asm volatile("tlbre %0,%1,0" : "=r" (w0) : "r" (i));
2656 asm volatile("tlbre %0,%1,1" : "=r" (w1) : "r" (i));
2657 asm volatile("tlbre %0,%1,2" : "=r" (w2) : "r" (i));
2658 printf("[%02x] %08x %08x %08x ", i, w0, w1, w2);
2659 if (w0 & PPC44x_TLB_VALID) {
2660 printf("V %08x -> %01x%08x %c%c%c%c%c",
2661 w0 & PPC44x_TLB_EPN_MASK,
2662 w1 & PPC44x_TLB_ERPN_MASK,
2663 w1 & PPC44x_TLB_RPN_MASK,
2664 (w2 & PPC44x_TLB_W) ? 'W' : 'w',
2665 (w2 & PPC44x_TLB_I) ? 'I' : 'i',
2666 (w2 & PPC44x_TLB_M) ? 'M' : 'm',
2667 (w2 & PPC44x_TLB_G) ? 'G' : 'g',
2668 (w2 & PPC44x_TLB_E) ? 'E' : 'e');
2669 }
2670 printf("\n");
2671 }
2672}
2673#endif /* CONFIG_44x */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002674
2675static void xmon_init(int enable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676{
Stephen Rothwellbbb68172006-11-30 11:44:09 +11002677#ifdef CONFIG_PPC_ISERIES
2678 if (firmware_has_feature(FW_FEATURE_ISERIES))
2679 return;
2680#endif
Olaf Heringb13cfd172005-08-04 19:26:42 +02002681 if (enable) {
2682 __debugger = xmon;
2683 __debugger_ipi = xmon_ipi;
2684 __debugger_bpt = xmon_bpt;
2685 __debugger_sstep = xmon_sstep;
2686 __debugger_iabr_match = xmon_iabr_match;
2687 __debugger_dabr_match = xmon_dabr_match;
2688 __debugger_fault_handler = xmon_fault_handler;
2689 } else {
2690 __debugger = NULL;
2691 __debugger_ipi = NULL;
2692 __debugger_bpt = NULL;
2693 __debugger_sstep = NULL;
2694 __debugger_iabr_match = NULL;
2695 __debugger_dabr_match = NULL;
2696 __debugger_fault_handler = NULL;
2697 }
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002698 xmon_map_scc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699}
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002700
2701#ifdef CONFIG_MAGIC_SYSRQ
David Howells7d12e782006-10-05 14:55:46 +01002702static void sysrq_handle_xmon(int key, struct tty_struct *tty)
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002703{
2704 /* ensure xmon is enabled */
2705 xmon_init(1);
David Howells7d12e782006-10-05 14:55:46 +01002706 debugger(get_irq_regs());
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002707}
2708
2709static struct sysrq_key_op sysrq_xmon_op =
2710{
2711 .handler = sysrq_handle_xmon,
2712 .help_msg = "Xmon",
2713 .action_msg = "Entering xmon",
2714};
2715
2716static int __init setup_xmon_sysrq(void)
2717{
Stephen Rothwellbbb68172006-11-30 11:44:09 +11002718#ifdef CONFIG_PPC_ISERIES
2719 if (firmware_has_feature(FW_FEATURE_ISERIES))
2720 return 0;
2721#endif
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002722 register_sysrq_key('x', &sysrq_xmon_op);
2723 return 0;
2724}
2725__initcall(setup_xmon_sysrq);
2726#endif /* CONFIG_MAGIC_SYSRQ */
Michael Ellerman476792832006-10-03 14:12:08 +10002727
Olaf Heringf5e6a282007-06-24 16:57:08 +10002728static int __initdata xmon_early, xmon_off;
Michael Ellerman476792832006-10-03 14:12:08 +10002729
2730static int __init early_parse_xmon(char *p)
2731{
2732 if (!p || strncmp(p, "early", 5) == 0) {
2733 /* just "xmon" is equivalent to "xmon=early" */
2734 xmon_init(1);
2735 xmon_early = 1;
2736 } else if (strncmp(p, "on", 2) == 0)
2737 xmon_init(1);
2738 else if (strncmp(p, "off", 3) == 0)
2739 xmon_off = 1;
2740 else if (strncmp(p, "nobt", 4) == 0)
2741 xmon_no_auto_backtrace = 1;
2742 else
2743 return 1;
2744
2745 return 0;
2746}
2747early_param("xmon", early_parse_xmon);
2748
2749void __init xmon_setup(void)
2750{
2751#ifdef CONFIG_XMON_DEFAULT
2752 if (!xmon_off)
2753 xmon_init(1);
2754#endif
2755 if (xmon_early)
2756 debugger(NULL);
2757}
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002758
Arnd Bergmanne0555952006-11-27 19:18:55 +01002759#ifdef CONFIG_SPU_BASE
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002760
2761struct spu_info {
2762 struct spu *spu;
2763 u64 saved_mfc_sr1_RW;
2764 u32 saved_spu_runcntl_RW;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002765 unsigned long dump_addr;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002766 u8 stopped_ok;
2767};
2768
2769#define XMON_NUM_SPUS 16 /* Enough for current hardware */
2770
2771static struct spu_info spu_info[XMON_NUM_SPUS];
2772
2773void xmon_register_spus(struct list_head *list)
2774{
2775 struct spu *spu;
2776
2777 list_for_each_entry(spu, list, full_list) {
2778 if (spu->number >= XMON_NUM_SPUS) {
2779 WARN_ON(1);
2780 continue;
2781 }
2782
2783 spu_info[spu->number].spu = spu;
2784 spu_info[spu->number].stopped_ok = 0;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002785 spu_info[spu->number].dump_addr = (unsigned long)
2786 spu_info[spu->number].spu->local_store;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002787 }
2788}
2789
2790static void stop_spus(void)
2791{
2792 struct spu *spu;
2793 int i;
2794 u64 tmp;
2795
2796 for (i = 0; i < XMON_NUM_SPUS; i++) {
2797 if (!spu_info[i].spu)
2798 continue;
2799
2800 if (setjmp(bus_error_jmp) == 0) {
2801 catch_memory_errors = 1;
2802 sync();
2803
2804 spu = spu_info[i].spu;
2805
2806 spu_info[i].saved_spu_runcntl_RW =
2807 in_be32(&spu->problem->spu_runcntl_RW);
2808
2809 tmp = spu_mfc_sr1_get(spu);
2810 spu_info[i].saved_mfc_sr1_RW = tmp;
2811
2812 tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
2813 spu_mfc_sr1_set(spu, tmp);
2814
2815 sync();
2816 __delay(200);
2817
2818 spu_info[i].stopped_ok = 1;
Michael Ellerman2a144422006-11-23 00:46:40 +01002819
2820 printf("Stopped spu %.2d (was %s)\n", i,
2821 spu_info[i].saved_spu_runcntl_RW ?
2822 "running" : "stopped");
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002823 } else {
2824 catch_memory_errors = 0;
2825 printf("*** Error stopping spu %.2d\n", i);
2826 }
2827 catch_memory_errors = 0;
2828 }
2829}
2830
2831static void restart_spus(void)
2832{
2833 struct spu *spu;
2834 int i;
2835
2836 for (i = 0; i < XMON_NUM_SPUS; i++) {
2837 if (!spu_info[i].spu)
2838 continue;
2839
2840 if (!spu_info[i].stopped_ok) {
2841 printf("*** Error, spu %d was not successfully stopped"
2842 ", not restarting\n", i);
2843 continue;
2844 }
2845
2846 if (setjmp(bus_error_jmp) == 0) {
2847 catch_memory_errors = 1;
2848 sync();
2849
2850 spu = spu_info[i].spu;
2851 spu_mfc_sr1_set(spu, spu_info[i].saved_mfc_sr1_RW);
2852 out_be32(&spu->problem->spu_runcntl_RW,
2853 spu_info[i].saved_spu_runcntl_RW);
2854
2855 sync();
2856 __delay(200);
2857
2858 printf("Restarted spu %.2d\n", i);
2859 } else {
2860 catch_memory_errors = 0;
2861 printf("*** Error restarting spu %.2d\n", i);
2862 }
2863 catch_memory_errors = 0;
2864 }
2865}
2866
Michael Ellermana8984972006-10-24 18:31:28 +02002867#define DUMP_WIDTH 23
Michael Ellerman437a0702006-11-23 00:46:39 +01002868#define DUMP_VALUE(format, field, value) \
Michael Ellermana8984972006-10-24 18:31:28 +02002869do { \
2870 if (setjmp(bus_error_jmp) == 0) { \
2871 catch_memory_errors = 1; \
2872 sync(); \
2873 printf(" %-*s = "format"\n", DUMP_WIDTH, \
Michael Ellerman437a0702006-11-23 00:46:39 +01002874 #field, value); \
Michael Ellermana8984972006-10-24 18:31:28 +02002875 sync(); \
2876 __delay(200); \
2877 } else { \
2878 catch_memory_errors = 0; \
2879 printf(" %-*s = *** Error reading field.\n", \
2880 DUMP_WIDTH, #field); \
2881 } \
2882 catch_memory_errors = 0; \
2883} while (0)
2884
Michael Ellerman437a0702006-11-23 00:46:39 +01002885#define DUMP_FIELD(obj, format, field) \
2886 DUMP_VALUE(format, field, obj->field)
2887
Michael Ellermana8984972006-10-24 18:31:28 +02002888static void dump_spu_fields(struct spu *spu)
2889{
2890 printf("Dumping spu fields at address %p:\n", spu);
2891
2892 DUMP_FIELD(spu, "0x%x", number);
2893 DUMP_FIELD(spu, "%s", name);
Michael Ellermana8984972006-10-24 18:31:28 +02002894 DUMP_FIELD(spu, "0x%lx", local_store_phys);
2895 DUMP_FIELD(spu, "0x%p", local_store);
2896 DUMP_FIELD(spu, "0x%lx", ls_size);
2897 DUMP_FIELD(spu, "0x%x", node);
2898 DUMP_FIELD(spu, "0x%lx", flags);
Michael Ellermana8984972006-10-24 18:31:28 +02002899 DUMP_FIELD(spu, "%d", class_0_pending);
Luke Browningf3d69e02008-04-27 18:41:55 +00002900 DUMP_FIELD(spu, "0x%lx", class_0_dar);
Luke Browningf3d69e02008-04-27 18:41:55 +00002901 DUMP_FIELD(spu, "0x%lx", class_1_dar);
2902 DUMP_FIELD(spu, "0x%lx", class_1_dsisr);
Michael Ellermana8984972006-10-24 18:31:28 +02002903 DUMP_FIELD(spu, "0x%lx", irqs[0]);
2904 DUMP_FIELD(spu, "0x%lx", irqs[1]);
2905 DUMP_FIELD(spu, "0x%lx", irqs[2]);
2906 DUMP_FIELD(spu, "0x%x", slb_replace);
2907 DUMP_FIELD(spu, "%d", pid);
Michael Ellermana8984972006-10-24 18:31:28 +02002908 DUMP_FIELD(spu, "0x%p", mm);
2909 DUMP_FIELD(spu, "0x%p", ctx);
2910 DUMP_FIELD(spu, "0x%p", rq);
2911 DUMP_FIELD(spu, "0x%p", timestamp);
2912 DUMP_FIELD(spu, "0x%lx", problem_phys);
2913 DUMP_FIELD(spu, "0x%p", problem);
Michael Ellerman437a0702006-11-23 00:46:39 +01002914 DUMP_VALUE("0x%x", problem->spu_runcntl_RW,
2915 in_be32(&spu->problem->spu_runcntl_RW));
2916 DUMP_VALUE("0x%x", problem->spu_status_R,
2917 in_be32(&spu->problem->spu_status_R));
2918 DUMP_VALUE("0x%x", problem->spu_npc_RW,
2919 in_be32(&spu->problem->spu_npc_RW));
Michael Ellermana8984972006-10-24 18:31:28 +02002920 DUMP_FIELD(spu, "0x%p", priv2);
Michael Ellermana9852392006-11-23 00:46:50 +01002921 DUMP_FIELD(spu, "0x%p", pdata);
Michael Ellermana8984972006-10-24 18:31:28 +02002922}
2923
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002924int
2925spu_inst_dump(unsigned long adr, long count, int praddr)
2926{
2927 return generic_inst_dump(adr, count, praddr, print_insn_spu);
2928}
2929
2930static void dump_spu_ls(unsigned long num, int subcmd)
Michael Ellerman24a24c82006-11-23 00:46:41 +01002931{
2932 unsigned long offset, addr, ls_addr;
2933
2934 if (setjmp(bus_error_jmp) == 0) {
2935 catch_memory_errors = 1;
2936 sync();
2937 ls_addr = (unsigned long)spu_info[num].spu->local_store;
2938 sync();
2939 __delay(200);
2940 } else {
2941 catch_memory_errors = 0;
2942 printf("*** Error: accessing spu info for spu %d\n", num);
2943 return;
2944 }
2945 catch_memory_errors = 0;
2946
2947 if (scanhex(&offset))
2948 addr = ls_addr + offset;
2949 else
2950 addr = spu_info[num].dump_addr;
2951
2952 if (addr >= ls_addr + LS_SIZE) {
2953 printf("*** Error: address outside of local store\n");
2954 return;
2955 }
2956
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002957 switch (subcmd) {
2958 case 'i':
2959 addr += spu_inst_dump(addr, 16, 1);
2960 last_cmd = "sdi\n";
2961 break;
2962 default:
2963 prdump(addr, 64);
2964 addr += 64;
2965 last_cmd = "sd\n";
2966 break;
2967 }
Michael Ellerman24a24c82006-11-23 00:46:41 +01002968
2969 spu_info[num].dump_addr = addr;
2970}
2971
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002972static int do_spu_cmd(void)
2973{
Michael Ellerman24a24c82006-11-23 00:46:41 +01002974 static unsigned long num = 0;
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002975 int cmd, subcmd = 0;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02002976
2977 cmd = inchar();
2978 switch (cmd) {
2979 case 's':
2980 stop_spus();
2981 break;
2982 case 'r':
2983 restart_spus();
2984 break;
Michael Ellerman24a24c82006-11-23 00:46:41 +01002985 case 'd':
Michael Ellermanaf89fb82006-11-23 00:46:44 +01002986 subcmd = inchar();
2987 if (isxdigit(subcmd) || subcmd == '\n')
2988 termch = subcmd;
2989 case 'f':
Michael Ellerman24a24c82006-11-23 00:46:41 +01002990 scanhex(&num);
2991 if (num >= XMON_NUM_SPUS || !spu_info[num].spu) {
Michael Ellermana8984972006-10-24 18:31:28 +02002992 printf("*** Error: invalid spu number\n");
Michael Ellerman24a24c82006-11-23 00:46:41 +01002993 return 0;
2994 }
2995
2996 switch (cmd) {
2997 case 'f':
2998 dump_spu_fields(spu_info[num].spu);
2999 break;
3000 default:
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003001 dump_spu_ls(num, subcmd);
Michael Ellerman24a24c82006-11-23 00:46:41 +01003002 break;
3003 }
3004
Michael Ellermana8984972006-10-24 18:31:28 +02003005 break;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003006 default:
3007 return -1;
3008 }
3009
3010 return 0;
3011}
Arnd Bergmanne0555952006-11-27 19:18:55 +01003012#else /* ! CONFIG_SPU_BASE */
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003013static int do_spu_cmd(void)
3014{
3015 return -1;
3016}
3017#endif