blob: e599259d84fc83773dcc01f7c81a362fdd777d65 [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>
Michael Ellermanca5dd392012-08-23 22:09:12 +000020#include <linux/kmsg_dump.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/cpumask.h>
Paul Gortmaker4b16f8e2011-07-22 18:24:23 -040022#include <linux/export.h>
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +110023#include <linux/sysrq.h>
Andrew Morton4694ca02005-11-13 16:06:50 -080024#include <linux/interrupt.h>
David Howells7d12e782006-10-05 14:55:46 +010025#include <linux/irq.h>
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -080026#include <linux/bug.h>
Anton Blancharda71d64b2014-08-05 14:55:00 +100027#include <linux/nmi.h>
Vincent Bernat05b981f2014-07-15 13:43:47 +020028#include <linux/ctype.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <asm/ptrace.h>
31#include <asm/string.h>
32#include <asm/prom.h>
33#include <asm/machdep.h>
Paul Mackerrasf78541dc2005-10-28 22:53:37 +100034#include <asm/xmon.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <asm/processor.h>
36#include <asm/pgtable.h>
37#include <asm/mmu.h>
38#include <asm/mmu_context.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <asm/cputable.h>
40#include <asm/rtas.h>
41#include <asm/sstep.h>
Paul Mackerrasf583ffc2006-10-10 11:47:07 +100042#include <asm/irq_regs.h>
Michael Ellermanff8a8f22006-10-24 18:31:27 +020043#include <asm/spu.h>
44#include <asm/spu_priv1.h>
Michael Neulingc3b75bd2008-01-18 15:50:30 +110045#include <asm/setjmp.h>
Anton Vorontsov322b4392008-12-17 10:08:55 +000046#include <asm/reg.h>
David Howellsae3a1972012-03-28 18:30:02 +010047#include <asm/debug.h>
Michael Neuling9422de32012-12-20 14:06:44 +000048#include <asm/hw_breakpoint.h>
Paul Mackerrasf78541dc2005-10-28 22:53:37 +100049
50#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include <asm/hvcall.h>
Paul Mackerrasf78541dc2005-10-28 22:53:37 +100052#include <asm/paca.h>
53#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
Anshuman Khandual1ad7d702014-11-28 10:06:42 +053055#if defined(CONFIG_PPC_SPLPAR)
56#include <asm/plpar_wrappers.h>
57#else
58static inline long plapr_set_ciabr(unsigned long ciabr) {return 0; };
59#endif
60
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#include "nonstdio.h"
Michael Ellermane0426042006-11-23 00:46:45 +010062#include "dis-asm.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
Linus Torvalds1da177e2005-04-16 15:20:36 -070064#ifdef CONFIG_SMP
Michael Ellerman1c8950f2008-05-08 14:27:17 +100065static cpumask_t cpus_in_xmon = CPU_MASK_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -070066static unsigned long xmon_taken = 1;
67static int xmon_owner;
68static int xmon_gate;
Michael Ellermanddadb6b2012-09-13 23:01:31 +000069#else
70#define xmon_owner 0
Linus Torvalds1da177e2005-04-16 15:20:36 -070071#endif /* CONFIG_SMP */
72
Anton Blanchard5be34922010-01-12 00:50:14 +000073static unsigned long in_xmon __read_mostly = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
75static unsigned long adrs;
76static int size = 1;
77#define MAX_DUMP (128 * 1024)
78static unsigned long ndump = 64;
79static unsigned long nidump = 16;
80static unsigned long ncsum = 4096;
81static int termch;
82static char tmpstr[128];
83
Linus Torvalds1da177e2005-04-16 15:20:36 -070084static long bus_error_jmp[JMP_BUF_LEN];
85static int catch_memory_errors;
86static long *xmon_fault_jmp[NR_CPUS];
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
88/* Breakpoint stuff */
89struct bpt {
90 unsigned long address;
91 unsigned int instr[2];
92 atomic_t ref_count;
93 int enabled;
94 unsigned long pad;
95};
96
97/* Bits in bpt.enabled */
Michael Ellermanabb90ee2014-12-01 16:54:13 +110098#define BP_CIABR 1
99#define BP_TRAP 2
100#define BP_DABR 4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
102#define NBPTS 256
103static struct bpt bpts[NBPTS];
104static struct bpt dabr;
105static struct bpt *iabr;
106static unsigned bpinstr = 0x7fe00008; /* trap */
107
108#define BP_NUM(bp) ((bp) - bpts + 1)
109
110/* Prototypes */
111static int cmds(struct pt_regs *);
112static int mread(unsigned long, void *, int);
113static int mwrite(unsigned long, void *, int);
114static int handle_fault(struct pt_regs *);
115static void byterev(unsigned char *, int);
116static void memex(void);
117static int bsesc(void);
118static void dump(void);
119static void prdump(unsigned long, long);
120static int ppc_inst_dump(unsigned long, long, int);
Vinay Sridharf312deb2009-05-14 23:13:07 +0000121static void dump_log_buf(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122static void backtrace(struct pt_regs *);
123static void excprint(struct pt_regs *);
124static void prregs(struct pt_regs *);
125static void memops(int);
126static void memlocate(void);
127static void memzcan(void);
128static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned);
129int skipbl(void);
130int scanhex(unsigned long *valp);
131static void scannl(void);
132static int hexdigit(int);
133void getstring(char *, int);
134static void flush_input(void);
135static int inchar(void);
136static void take_input(char *);
137static unsigned long read_spr(int);
138static void write_spr(int, unsigned long);
139static void super_regs(void);
140static void remove_bpts(void);
141static void insert_bpts(void);
142static void remove_cpu_bpts(void);
143static void insert_cpu_bpts(void);
144static struct bpt *at_breakpoint(unsigned long pc);
145static struct bpt *in_breakpoint_table(unsigned long pc, unsigned long *offp);
146static int do_step(struct pt_regs *);
147static void bpt_cmds(void);
148static void cacheflush(void);
149static int cpu_cmd(void);
150static void csum(void);
151static void bootcmds(void);
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000152static void proccall(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153void dump_segments(void);
154static void symbol_lookup(void);
Olaf Hering26c8af52006-09-08 16:29:21 +0200155static void xmon_show_stack(unsigned long sp, unsigned long lr,
156 unsigned long pc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157static void xmon_print_symbol(unsigned long address, const char *mid,
158 const char *after);
159static const char *getvecname(unsigned long vec);
160
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200161static int do_spu_cmd(void);
162
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100163#ifdef CONFIG_44x
164static void dump_tlb_44x(void);
165#endif
Benjamin Herrenschmidt03247152010-07-09 15:34:50 +1000166#ifdef CONFIG_PPC_BOOK3E
167static void dump_tlb_book3e(void);
168#endif
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100169
Michael Ellerman9f1067c22008-05-08 14:27:16 +1000170static int xmon_no_auto_backtrace;
Olaf Hering26c8af52006-09-08 16:29:21 +0200171
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000172extern void xmon_enter(void);
173extern void xmon_leave(void);
174
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000175#ifdef CONFIG_PPC64
176#define REG "%.16lx"
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000177#else
178#define REG "%.8lx"
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000179#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
Philippe Bergheaud72eceef2013-12-02 10:10:12 +0100181#ifdef __LITTLE_ENDIAN__
182#define GETWORD(v) (((v)[3] << 24) + ((v)[2] << 16) + ((v)[1] << 8) + (v)[0])
183#else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])
Philippe Bergheaud72eceef2013-12-02 10:10:12 +0100185#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187static char *help_string = "\
188Commands:\n\
189 b show breakpoints\n\
190 bd set data breakpoint\n\
191 bi set instruction breakpoint\n\
192 bc clear breakpoint\n"
193#ifdef CONFIG_SMP
194 "\
195 c print cpus stopped in xmon\n\
196 c# try to switch to cpu number h (in hex)\n"
197#endif
198 "\
199 C checksum\n\
200 d dump bytes\n\
201 di dump instructions\n\
202 df dump float values\n\
203 dd dump double values\n\
Michael Ellermanddadb6b2012-09-13 23:01:31 +0000204 dl dump the kernel log buffer\n"
205#ifdef CONFIG_PPC64
206 "\
207 dp[#] dump paca for current cpu, or cpu #\n\
208 dpa dump paca for all possible cpus\n"
209#endif
210 "\
Olaf Hering7e5b5932006-03-08 20:40:28 +0100211 dr dump stream of raw bytes\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 e print exception information\n\
213 f flush cache\n\
214 la lookup symbol+offset of specified address\n\
215 ls lookup address of specified symbol\n\
216 m examine/change memory\n\
217 mm move a block of memory\n\
218 ms set a block of memory\n\
219 md compare two blocks of memory\n\
220 ml locate a block of memory\n\
221 mz zero a block of memory\n\
222 mi show information about memory allocation\n\
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000223 p call a procedure\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 r print registers\n\
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200225 s single step\n"
Arnd Bergmanne0555952006-11-27 19:18:55 +0100226#ifdef CONFIG_SPU_BASE
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200227" ss stop execution on all spus\n\
Michael Ellermana8984972006-10-24 18:31:28 +0200228 sr restore execution on stopped spus\n\
Michael Ellerman24a24c82006-11-23 00:46:41 +0100229 sf # dump spu fields for spu # (in hex)\n\
Michael Ellermanc99176a2007-02-26 18:14:06 +0900230 sd # dump spu local store for spu # (in hex)\n\
Michael Ellermanaf89fb82006-11-23 00:46:44 +0100231 sdi # disassemble spu local store for spu # (in hex)\n"
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200232#endif
233" S print special registers\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 t print backtrace\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 x exit monitor and recover\n\
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000236 X exit monitor and dont recover\n"
Jimi Xenidis79873e82011-09-29 11:25:10 +0000237#if defined(CONFIG_PPC64) && !defined(CONFIG_PPC_BOOK3E)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000238" u dump segment table or SLB\n"
Jimi Xenidis79873e82011-09-29 11:25:10 +0000239#elif defined(CONFIG_PPC_STD_MMU_32)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000240" u dump segment registers\n"
Jimi Xenidis79873e82011-09-29 11:25:10 +0000241#elif defined(CONFIG_44x) || defined(CONFIG_PPC_BOOK3E)
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100242" u dump TLB\n"
243#endif
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000244" ? help\n"
245" zr reboot\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 zh halt\n"
247;
248
249static struct pt_regs *xmon_regs;
250
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000251static inline void sync(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
253 asm volatile("sync; isync");
254}
255
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000256static inline void store_inst(void *p)
257{
258 asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p));
259}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000261static inline void cflush(void *p)
262{
263 asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p));
264}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000266static inline void cinval(void *p)
267{
268 asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p));
269}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270
Anshuman Khandual1ad7d702014-11-28 10:06:42 +0530271/**
272 * write_ciabr() - write the CIABR SPR
273 * @ciabr: The value to write.
274 *
275 * This function writes a value to the CIARB register either directly
276 * through mtspr instruction if the kernel is in HV privilege mode or
277 * call a hypervisor function to achieve the same in case the kernel
278 * is in supervisor privilege mode.
279 */
280static void write_ciabr(unsigned long ciabr)
281{
282 if (!cpu_has_feature(CPU_FTR_ARCH_207S))
283 return;
284
285 if (cpu_has_feature(CPU_FTR_HVMODE)) {
286 mtspr(SPRN_CIABR, ciabr);
287 return;
288 }
289 plapr_set_ciabr(ciabr);
290}
291
292/**
293 * set_ciabr() - set the CIABR
294 * @addr: The value to set.
295 *
296 * This function sets the correct privilege value into the the HW
297 * breakpoint address before writing it up in the CIABR register.
298 */
299static void set_ciabr(unsigned long addr)
300{
301 addr &= ~CIABR_PRIV;
302
303 if (cpu_has_feature(CPU_FTR_HVMODE))
304 addr |= CIABR_PRIV_HYPER;
305 else
306 addr |= CIABR_PRIV_SUPER;
307 write_ciabr(addr);
308}
309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310/*
311 * Disable surveillance (the service processor watchdog function)
312 * while we are in xmon.
313 * XXX we should re-enable it when we leave. :)
314 */
315#define SURVEILLANCE_TOKEN 9000
316
317static inline void disable_surveillance(void)
318{
319#ifdef CONFIG_PPC_PSERIES
320 /* Since this can't be a module, args should end up below 4GB. */
321 static struct rtas_args args;
322
323 /*
324 * At this point we have got all the cpus we can into
325 * xmon, so there is hopefully no other cpu calling RTAS
326 * at the moment, even though we don't take rtas.lock.
327 * If we did try to take rtas.lock there would be a
328 * real possibility of deadlock.
329 */
330 args.token = rtas_token("set-indicator");
331 if (args.token == RTAS_UNKNOWN_SERVICE)
332 return;
Laurent Dufoure6eb2eb2015-01-15 18:23:47 +0100333 args.token = cpu_to_be32(args.token);
Laurent Dufour3b8a3c02014-11-24 15:07:53 +0100334 args.nargs = cpu_to_be32(3);
335 args.nret = cpu_to_be32(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 args.rets = &args.args[3];
Laurent Dufour3b8a3c02014-11-24 15:07:53 +0100337 args.args[0] = cpu_to_be32(SURVEILLANCE_TOKEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 args.args[1] = 0;
339 args.args[2] = 0;
340 enter_rtas(__pa(&args));
341#endif /* CONFIG_PPC_PSERIES */
342}
343
344#ifdef CONFIG_SMP
345static int xmon_speaker;
346
347static void get_output_lock(void)
348{
349 int me = smp_processor_id() + 0x100;
350 int last_speaker = 0, prev;
351 long timeout;
352
353 if (xmon_speaker == me)
354 return;
Michael Ellerman730efb62013-12-23 23:46:04 +1100355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 for (;;) {
Michael Ellerman730efb62013-12-23 23:46:04 +1100357 last_speaker = cmpxchg(&xmon_speaker, 0, me);
358 if (last_speaker == 0)
359 return;
360
Michael Ellerman15075892013-12-23 23:46:05 +1100361 /*
362 * Wait a full second for the lock, we might be on a slow
363 * console, but check every 100us.
364 */
365 timeout = 10000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 while (xmon_speaker == last_speaker) {
Michael Ellerman15075892013-12-23 23:46:05 +1100367 if (--timeout > 0) {
368 udelay(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 continue;
Michael Ellerman15075892013-12-23 23:46:05 +1100370 }
371
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 /* hostile takeover */
373 prev = cmpxchg(&xmon_speaker, last_speaker, me);
374 if (prev == last_speaker)
375 return;
376 break;
377 }
378 }
379}
380
381static void release_output_lock(void)
382{
383 xmon_speaker = 0;
384}
Michael Ellerman1c8950f2008-05-08 14:27:17 +1000385
386int cpus_are_in_xmon(void)
387{
KOSAKI Motohiro104699c2011-04-28 05:07:23 +0000388 return !cpumask_empty(&cpus_in_xmon);
Michael Ellerman1c8950f2008-05-08 14:27:17 +1000389}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390#endif
391
Josh Boyerdaf8f402009-09-23 03:51:04 +0000392static inline int unrecoverable_excp(struct pt_regs *regs)
393{
Jimi Xenidis08f6d6a2011-09-29 12:05:28 +0000394#if defined(CONFIG_4xx) || defined(CONFIG_PPC_BOOK3E)
Jimi Xenidis66857b32011-09-23 05:40:46 +0000395 /* We have no MSR_RI bit on 4xx or Book3e, so we simply return false */
Josh Boyerdaf8f402009-09-23 03:51:04 +0000396 return 0;
397#else
398 return ((regs->msr & MSR_RI) == 0);
399#endif
400}
401
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000402static int xmon_core(struct pt_regs *regs, int fromipi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403{
404 int cmd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 struct bpt *bp;
406 long recurse_jmp[JMP_BUF_LEN];
407 unsigned long offset;
Anton Blanchardf13659e2007-03-21 01:48:34 +1100408 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409#ifdef CONFIG_SMP
410 int cpu;
411 int secondary;
412 unsigned long timeout;
413#endif
414
Anton Blanchardf13659e2007-03-21 01:48:34 +1100415 local_irq_save(flags);
Anton Blancharda71d64b2014-08-05 14:55:00 +1000416 hard_irq_disable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
418 bp = in_breakpoint_table(regs->nip, &offset);
419 if (bp != NULL) {
420 regs->nip = bp->address + offset;
421 atomic_dec(&bp->ref_count);
422 }
423
424 remove_cpu_bpts();
425
426#ifdef CONFIG_SMP
427 cpu = smp_processor_id();
KOSAKI Motohiro104699c2011-04-28 05:07:23 +0000428 if (cpumask_test_cpu(cpu, &cpus_in_xmon)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 get_output_lock();
430 excprint(regs);
431 printf("cpu 0x%x: Exception %lx %s in xmon, "
432 "returning to main loop\n",
433 cpu, regs->trap, getvecname(TRAP(regs)));
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000434 release_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 longjmp(xmon_fault_jmp[cpu], 1);
436 }
437
438 if (setjmp(recurse_jmp) != 0) {
439 if (!in_xmon || !xmon_gate) {
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000440 get_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 printf("xmon: WARNING: bad recursive fault "
442 "on cpu 0x%x\n", cpu);
Haren Myneni5cb4cc02005-08-03 15:08:18 +1000443 release_output_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 goto waiting;
445 }
446 secondary = !(xmon_taken && cpu == xmon_owner);
447 goto cmdloop;
448 }
449
450 xmon_fault_jmp[cpu] = recurse_jmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
452 bp = NULL;
Michael Ellerman9f0b0792011-04-07 21:56:03 +0000453 if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) == (MSR_IR|MSR_64BIT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 bp = at_breakpoint(regs->nip);
Josh Boyerdaf8f402009-09-23 03:51:04 +0000455 if (bp || unrecoverable_excp(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 fromipi = 0;
457
458 if (!fromipi) {
459 get_output_lock();
460 excprint(regs);
461 if (bp) {
Michael Ellerman736256e2014-05-26 21:02:14 +1000462 printf("cpu 0x%x stopped at breakpoint 0x%lx (",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 cpu, BP_NUM(bp));
464 xmon_print_symbol(regs->nip, " ", ")\n");
465 }
Josh Boyerdaf8f402009-09-23 03:51:04 +0000466 if (unrecoverable_excp(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 printf("WARNING: exception is not recoverable, "
468 "can't continue\n");
469 release_output_lock();
470 }
471
Michael Ellermand2b496e2013-12-23 23:46:06 +1100472 cpumask_set_cpu(cpu, &cpus_in_xmon);
473
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 waiting:
475 secondary = 1;
476 while (secondary && !xmon_gate) {
477 if (in_xmon == 0) {
478 if (fromipi)
479 goto leave;
480 secondary = test_and_set_bit(0, &in_xmon);
481 }
482 barrier();
483 }
484
485 if (!secondary && !xmon_gate) {
486 /* we are the first cpu to come in */
487 /* interrupt other cpu(s) */
488 int ncpus = num_online_cpus();
489
490 xmon_owner = cpu;
491 mb();
492 if (ncpus > 1) {
Milton Millere0476372011-05-10 19:29:06 +0000493 smp_send_debugger_break();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 /* wait for other cpus to come in */
495 for (timeout = 100000000; timeout != 0; --timeout) {
KOSAKI Motohiro104699c2011-04-28 05:07:23 +0000496 if (cpumask_weight(&cpus_in_xmon) >= ncpus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 break;
498 barrier();
499 }
500 }
501 remove_bpts();
502 disable_surveillance();
503 /* for breakpoint or single step, print the current instr. */
504 if (bp || TRAP(regs) == 0xd00)
505 ppc_inst_dump(regs->nip, 1, 0);
506 printf("enter ? for help\n");
507 mb();
508 xmon_gate = 1;
509 barrier();
510 }
511
512 cmdloop:
513 while (in_xmon) {
514 if (secondary) {
515 if (cpu == xmon_owner) {
516 if (!test_and_set_bit(0, &xmon_taken)) {
517 secondary = 0;
518 continue;
519 }
520 /* missed it */
521 while (cpu == xmon_owner)
522 barrier();
523 }
524 barrier();
525 } else {
526 cmd = cmds(regs);
527 if (cmd != 0) {
528 /* exiting xmon */
529 insert_bpts();
530 xmon_gate = 0;
531 wmb();
532 in_xmon = 0;
533 break;
534 }
535 /* have switched to some other cpu */
536 secondary = 1;
537 }
538 }
539 leave:
KOSAKI Motohiro104699c2011-04-28 05:07:23 +0000540 cpumask_clear_cpu(cpu, &cpus_in_xmon);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 xmon_fault_jmp[cpu] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542#else
543 /* UP is simple... */
544 if (in_xmon) {
545 printf("Exception %lx %s in xmon, returning to main loop\n",
546 regs->trap, getvecname(TRAP(regs)));
547 longjmp(xmon_fault_jmp[0], 1);
548 }
549 if (setjmp(recurse_jmp) == 0) {
550 xmon_fault_jmp[0] = recurse_jmp;
551 in_xmon = 1;
552
553 excprint(regs);
554 bp = at_breakpoint(regs->nip);
555 if (bp) {
Michael Ellerman736256e2014-05-26 21:02:14 +1000556 printf("Stopped at breakpoint %lx (", BP_NUM(bp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 xmon_print_symbol(regs->nip, " ", ")\n");
558 }
Josh Boyerdaf8f402009-09-23 03:51:04 +0000559 if (unrecoverable_excp(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 printf("WARNING: exception is not recoverable, "
561 "can't continue\n");
562 remove_bpts();
563 disable_surveillance();
564 /* for breakpoint or single step, print the current instr. */
565 if (bp || TRAP(regs) == 0xd00)
566 ppc_inst_dump(regs->nip, 1, 0);
567 printf("enter ? for help\n");
568 }
569
570 cmd = cmds(regs);
571
572 insert_bpts();
573 in_xmon = 0;
574#endif
575
Josh Boyercdd39042009-10-05 04:46:05 +0000576#ifdef CONFIG_BOOKE
577 if (regs->msr & MSR_DE) {
578 bp = at_breakpoint(regs->nip);
579 if (bp != NULL) {
580 regs->nip = (unsigned long) &bp->instr[0];
581 atomic_inc(&bp->ref_count);
582 }
583 }
584#else
Michael Ellerman9f0b0792011-04-07 21:56:03 +0000585 if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) == (MSR_IR|MSR_64BIT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 bp = at_breakpoint(regs->nip);
587 if (bp != NULL) {
588 int stepped = emulate_step(regs, bp->instr[0]);
589 if (stepped == 0) {
590 regs->nip = (unsigned long) &bp->instr[0];
591 atomic_inc(&bp->ref_count);
592 } else if (stepped < 0) {
593 printf("Couldn't single-step %s instruction\n",
594 (IS_RFID(bp->instr[0])? "rfid": "mtmsrd"));
595 }
596 }
597 }
Josh Boyercdd39042009-10-05 04:46:05 +0000598#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 insert_cpu_bpts();
600
Anton Blancharda71d64b2014-08-05 14:55:00 +1000601 touch_nmi_watchdog();
Anton Blanchardf13659e2007-03-21 01:48:34 +1100602 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Paul Mackerras0a730ae2006-10-03 21:32:49 +1000604 return cmd != 'X' && cmd != EOF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605}
606
607int xmon(struct pt_regs *excp)
608{
609 struct pt_regs regs;
610
611 if (excp == NULL) {
Anton Vorontsov322b4392008-12-17 10:08:55 +0000612 ppc_save_regs(&regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 excp = &regs;
614 }
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200615
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 return xmon_core(excp, 0);
617}
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000618EXPORT_SYMBOL(xmon);
619
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000620irqreturn_t xmon_irq(int irq, void *d)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000621{
622 unsigned long flags;
623 local_irq_save(flags);
624 printf("Keyboard interrupt\n");
Paul Mackerrasf583ffc2006-10-10 11:47:07 +1000625 xmon(get_irq_regs());
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000626 local_irq_restore(flags);
627 return IRQ_HANDLED;
628}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000630static int xmon_bpt(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631{
632 struct bpt *bp;
633 unsigned long offset;
634
Michael Ellerman9f0b0792011-04-07 21:56:03 +0000635 if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 return 0;
637
638 /* Are we at the trap at bp->instr[1] for some bp? */
639 bp = in_breakpoint_table(regs->nip, &offset);
640 if (bp != NULL && offset == 4) {
641 regs->nip = bp->address + 4;
642 atomic_dec(&bp->ref_count);
643 return 1;
644 }
645
646 /* Are we at a breakpoint? */
647 bp = at_breakpoint(regs->nip);
648 if (!bp)
649 return 0;
650
651 xmon_core(regs, 0);
652
653 return 1;
654}
655
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000656static int xmon_sstep(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657{
658 if (user_mode(regs))
659 return 0;
660 xmon_core(regs, 0);
661 return 1;
662}
663
Michael Neuling9422de32012-12-20 14:06:44 +0000664static int xmon_break_match(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665{
Michael Ellerman9f0b0792011-04-07 21:56:03 +0000666 if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 return 0;
Anton Blanchardfd9648d2005-09-10 16:01:11 +1000668 if (dabr.enabled == 0)
669 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 xmon_core(regs, 0);
671 return 1;
672}
673
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000674static int xmon_iabr_match(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675{
Michael Ellerman9f0b0792011-04-07 21:56:03 +0000676 if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return 0;
Michael Ellerman9f1067c22008-05-08 14:27:16 +1000678 if (iabr == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 return 0;
680 xmon_core(regs, 0);
681 return 1;
682}
683
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000684static int xmon_ipi(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685{
686#ifdef CONFIG_SMP
KOSAKI Motohiro104699c2011-04-28 05:07:23 +0000687 if (in_xmon && !cpumask_test_cpu(smp_processor_id(), &cpus_in_xmon))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 xmon_core(regs, 1);
689#endif
690 return 0;
691}
692
Arnd Bergmannb0da9852006-01-11 00:00:05 +0000693static int xmon_fault_handler(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694{
695 struct bpt *bp;
696 unsigned long offset;
697
698 if (in_xmon && catch_memory_errors)
699 handle_fault(regs); /* doesn't return */
700
Michael Ellerman9f0b0792011-04-07 21:56:03 +0000701 if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) == (MSR_IR|MSR_64BIT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 bp = in_breakpoint_table(regs->nip, &offset);
703 if (bp != NULL) {
704 regs->nip = bp->address + offset;
705 atomic_dec(&bp->ref_count);
706 }
707 }
708
709 return 0;
710}
711
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712static struct bpt *at_breakpoint(unsigned long pc)
713{
714 int i;
715 struct bpt *bp;
716
717 bp = bpts;
718 for (i = 0; i < NBPTS; ++i, ++bp)
719 if (bp->enabled && pc == bp->address)
720 return bp;
721 return NULL;
722}
723
724static struct bpt *in_breakpoint_table(unsigned long nip, unsigned long *offp)
725{
726 unsigned long off;
727
728 off = nip - (unsigned long) bpts;
729 if (off >= sizeof(bpts))
730 return NULL;
731 off %= sizeof(struct bpt);
732 if (off != offsetof(struct bpt, instr[0])
733 && off != offsetof(struct bpt, instr[1]))
734 return NULL;
735 *offp = off - offsetof(struct bpt, instr[0]);
736 return (struct bpt *) (nip - off);
737}
738
739static struct bpt *new_breakpoint(unsigned long a)
740{
741 struct bpt *bp;
742
743 a &= ~3UL;
744 bp = at_breakpoint(a);
745 if (bp)
746 return bp;
747
748 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
749 if (!bp->enabled && atomic_read(&bp->ref_count) == 0) {
750 bp->address = a;
751 bp->instr[1] = bpinstr;
752 store_inst(&bp->instr[1]);
753 return bp;
754 }
755 }
756
757 printf("Sorry, no free breakpoints. Please clear one first.\n");
758 return NULL;
759}
760
761static void insert_bpts(void)
762{
763 int i;
764 struct bpt *bp;
765
766 bp = bpts;
767 for (i = 0; i < NBPTS; ++i, ++bp) {
Michael Ellermanabb90ee2014-12-01 16:54:13 +1100768 if ((bp->enabled & (BP_TRAP|BP_CIABR)) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 continue;
770 if (mread(bp->address, &bp->instr[0], 4) != 4) {
771 printf("Couldn't read instruction at %lx, "
772 "disabling breakpoint there\n", bp->address);
773 bp->enabled = 0;
774 continue;
775 }
776 if (IS_MTMSRD(bp->instr[0]) || IS_RFID(bp->instr[0])) {
777 printf("Breakpoint at %lx is on an mtmsrd or rfid "
778 "instruction, disabling it\n", bp->address);
779 bp->enabled = 0;
780 continue;
781 }
782 store_inst(&bp->instr[0]);
Michael Ellermanabb90ee2014-12-01 16:54:13 +1100783 if (bp->enabled & BP_CIABR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 continue;
785 if (mwrite(bp->address, &bpinstr, 4) != 4) {
786 printf("Couldn't write instruction at %lx, "
787 "disabling breakpoint there\n", bp->address);
788 bp->enabled &= ~BP_TRAP;
789 continue;
790 }
791 store_inst((void *)bp->address);
792 }
793}
794
795static void insert_cpu_bpts(void)
796{
Michael Neuling9422de32012-12-20 14:06:44 +0000797 struct arch_hw_breakpoint brk;
798
799 if (dabr.enabled) {
800 brk.address = dabr.address;
801 brk.type = (dabr.enabled & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
802 brk.len = 8;
Paul Gortmaker21f58502014-04-29 15:25:17 -0400803 __set_breakpoint(&brk);
Michael Neuling9422de32012-12-20 14:06:44 +0000804 }
Anshuman Khandual1ad7d702014-11-28 10:06:42 +0530805
806 if (iabr)
807 set_ciabr(iabr->address);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808}
809
810static void remove_bpts(void)
811{
812 int i;
813 struct bpt *bp;
814 unsigned instr;
815
816 bp = bpts;
817 for (i = 0; i < NBPTS; ++i, ++bp) {
Michael Ellermanabb90ee2014-12-01 16:54:13 +1100818 if ((bp->enabled & (BP_TRAP|BP_CIABR)) != BP_TRAP)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 continue;
820 if (mread(bp->address, &instr, 4) == 4
821 && instr == bpinstr
822 && mwrite(bp->address, &bp->instr, 4) != 4)
823 printf("Couldn't remove breakpoint at %lx\n",
824 bp->address);
825 else
826 store_inst((void *)bp->address);
827 }
828}
829
830static void remove_cpu_bpts(void)
831{
Michael Neuling9422de32012-12-20 14:06:44 +0000832 hw_breakpoint_disable();
Anshuman Khandual1ad7d702014-11-28 10:06:42 +0530833 write_ciabr(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834}
835
836/* Command interpreting routine */
837static char *last_cmd;
838
839static int
840cmds(struct pt_regs *excp)
841{
842 int cmd = 0;
843
844 last_cmd = NULL;
845 xmon_regs = excp;
Olaf Hering26c8af52006-09-08 16:29:21 +0200846
847 if (!xmon_no_auto_backtrace) {
848 xmon_no_auto_backtrace = 1;
849 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
850 }
851
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 for(;;) {
853#ifdef CONFIG_SMP
854 printf("%x:", smp_processor_id());
855#endif /* CONFIG_SMP */
856 printf("mon> ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 flush_input();
858 termch = 0;
859 cmd = skipbl();
860 if( cmd == '\n' ) {
861 if (last_cmd == NULL)
862 continue;
863 take_input(last_cmd);
864 last_cmd = NULL;
865 cmd = inchar();
866 }
867 switch (cmd) {
868 case 'm':
869 cmd = inchar();
870 switch (cmd) {
871 case 'm':
872 case 's':
873 case 'd':
874 memops(cmd);
875 break;
876 case 'l':
877 memlocate();
878 break;
879 case 'z':
880 memzcan();
881 break;
882 case 'i':
David Rientjesb2b755b2011-03-24 15:18:15 -0700883 show_mem(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 break;
885 default:
886 termch = cmd;
887 memex();
888 }
889 break;
890 case 'd':
891 dump();
892 break;
893 case 'l':
894 symbol_lookup();
895 break;
896 case 'r':
897 prregs(excp); /* print regs */
898 break;
899 case 'e':
900 excprint(excp);
901 break;
902 case 'S':
903 super_regs();
904 break;
905 case 't':
906 backtrace(excp);
907 break;
908 case 'f':
909 cacheflush();
910 break;
911 case 's':
Michael Ellermanff8a8f22006-10-24 18:31:27 +0200912 if (do_spu_cmd() == 0)
913 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 if (do_step(excp))
915 return cmd;
916 break;
917 case 'x':
918 case 'X':
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100919 return cmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 case EOF:
Benjamin Herrenschmidtbb6b9b22005-11-30 16:54:12 +1100921 printf(" <no input ...>\n");
922 mdelay(2000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 return cmd;
924 case '?':
Ishizaki Kou4d404ed2007-07-18 19:26:40 +1000925 xmon_puts(help_string);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 case 'b':
928 bpt_cmds();
929 break;
930 case 'C':
931 csum();
932 break;
933 case 'c':
934 if (cpu_cmd())
935 return 0;
936 break;
937 case 'z':
938 bootcmds();
939 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000940 case 'p':
941 proccall();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +1000943#ifdef CONFIG_PPC_STD_MMU
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 case 'u':
945 dump_segments();
946 break;
Michael Ellermand8ee6f32014-11-12 16:54:54 +1100947#elif defined(CONFIG_44x)
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +1100948 case 'u':
949 dump_tlb_44x();
950 break;
Jimi Xenidis79873e82011-09-29 11:25:10 +0000951#elif defined(CONFIG_PPC_BOOK3E)
Benjamin Herrenschmidt03247152010-07-09 15:34:50 +1000952 case 'u':
953 dump_tlb_book3e();
954 break;
955#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 default:
957 printf("Unrecognized command: ");
Michael Ellermane3bc8042012-08-23 22:09:13 +0000958 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 if (' ' < cmd && cmd <= '~')
960 putchar(cmd);
961 else
962 printf("\\x%x", cmd);
963 cmd = inchar();
Michael Ellermane3bc8042012-08-23 22:09:13 +0000964 } while (cmd != '\n');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 printf(" (type ? for help)\n");
966 break;
967 }
968 }
969}
970
Josh Boyercdd39042009-10-05 04:46:05 +0000971#ifdef CONFIG_BOOKE
972static int do_step(struct pt_regs *regs)
973{
974 regs->msr |= MSR_DE;
975 mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC | DBCR0_IDM);
976 return 1;
977}
978#else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979/*
980 * Step a single instruction.
981 * Some instructions we emulate, others we execute with MSR_SE set.
982 */
983static int do_step(struct pt_regs *regs)
984{
985 unsigned int instr;
986 int stepped;
987
988 /* check we are in 64-bit kernel mode, translation enabled */
Michael Ellerman9f0b0792011-04-07 21:56:03 +0000989 if ((regs->msr & (MSR_64BIT|MSR_PR|MSR_IR)) == (MSR_64BIT|MSR_IR)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 if (mread(regs->nip, &instr, 4) == 4) {
991 stepped = emulate_step(regs, instr);
992 if (stepped < 0) {
993 printf("Couldn't single-step %s instruction\n",
994 (IS_RFID(instr)? "rfid": "mtmsrd"));
995 return 0;
996 }
997 if (stepped > 0) {
998 regs->trap = 0xd00 | (regs->trap & 1);
999 printf("stepped to ");
1000 xmon_print_symbol(regs->nip, " ", "\n");
1001 ppc_inst_dump(regs->nip, 1, 0);
1002 return 0;
1003 }
1004 }
1005 }
1006 regs->msr |= MSR_SE;
1007 return 1;
1008}
Josh Boyercdd39042009-10-05 04:46:05 +00001009#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010
1011static void bootcmds(void)
1012{
1013 int cmd;
1014
1015 cmd = inchar();
1016 if (cmd == 'r')
1017 ppc_md.restart(NULL);
1018 else if (cmd == 'h')
1019 ppc_md.halt();
1020 else if (cmd == 'p')
Alexander Graf9178ba22014-10-13 16:01:09 +02001021 if (pm_power_off)
1022 pm_power_off();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023}
1024
1025static int cpu_cmd(void)
1026{
1027#ifdef CONFIG_SMP
Paul Mackerrasfd3bb912013-09-03 20:16:23 +10001028 unsigned long cpu, first_cpu, last_cpu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 int timeout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030
1031 if (!scanhex(&cpu)) {
1032 /* print cpus waiting or in xmon */
1033 printf("cpus stopped:");
Paul Mackerrasfd3bb912013-09-03 20:16:23 +10001034 last_cpu = first_cpu = NR_CPUS;
Anton Blanchardbc1d7702012-06-28 19:28:57 +00001035 for_each_possible_cpu(cpu) {
KOSAKI Motohiro104699c2011-04-28 05:07:23 +00001036 if (cpumask_test_cpu(cpu, &cpus_in_xmon)) {
Paul Mackerrasfd3bb912013-09-03 20:16:23 +10001037 if (cpu == last_cpu + 1) {
1038 last_cpu = cpu;
1039 } else {
1040 if (last_cpu != first_cpu)
Michael Ellerman736256e2014-05-26 21:02:14 +10001041 printf("-0x%lx", last_cpu);
Paul Mackerrasfd3bb912013-09-03 20:16:23 +10001042 last_cpu = first_cpu = cpu;
Michael Ellerman736256e2014-05-26 21:02:14 +10001043 printf(" 0x%lx", cpu);
Paul Mackerrasfd3bb912013-09-03 20:16:23 +10001044 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 }
1046 }
Paul Mackerrasfd3bb912013-09-03 20:16:23 +10001047 if (last_cpu != first_cpu)
Michael Ellerman736256e2014-05-26 21:02:14 +10001048 printf("-0x%lx", last_cpu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 printf("\n");
1050 return 0;
1051 }
1052 /* try to switch to cpu specified */
KOSAKI Motohiro104699c2011-04-28 05:07:23 +00001053 if (!cpumask_test_cpu(cpu, &cpus_in_xmon)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 printf("cpu 0x%x isn't in xmon\n", cpu);
1055 return 0;
1056 }
1057 xmon_taken = 0;
1058 mb();
1059 xmon_owner = cpu;
1060 timeout = 10000000;
1061 while (!xmon_taken) {
1062 if (--timeout == 0) {
1063 if (test_and_set_bit(0, &xmon_taken))
1064 break;
1065 /* take control back */
1066 mb();
1067 xmon_owner = smp_processor_id();
Michael Ellerman736256e2014-05-26 21:02:14 +10001068 printf("cpu 0x%x didn't take control\n", cpu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 return 0;
1070 }
1071 barrier();
1072 }
1073 return 1;
1074#else
1075 return 0;
1076#endif /* CONFIG_SMP */
1077}
1078
1079static unsigned short fcstab[256] = {
1080 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
1081 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
1082 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
1083 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
1084 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
1085 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
1086 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
1087 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
1088 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
1089 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
1090 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
1091 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
1092 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
1093 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
1094 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
1095 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
1096 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
1097 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
1098 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
1099 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
1100 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
1101 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
1102 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
1103 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
1104 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
1105 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
1106 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
1107 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
1108 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
1109 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
1110 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
1111 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
1112};
1113
1114#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
1115
1116static void
1117csum(void)
1118{
1119 unsigned int i;
1120 unsigned short fcs;
1121 unsigned char v;
1122
1123 if (!scanhex(&adrs))
1124 return;
1125 if (!scanhex(&ncsum))
1126 return;
1127 fcs = 0xffff;
1128 for (i = 0; i < ncsum; ++i) {
1129 if (mread(adrs+i, &v, 1) == 0) {
Michael Ellerman736256e2014-05-26 21:02:14 +10001130 printf("csum stopped at "REG"\n", adrs+i);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 break;
1132 }
1133 fcs = FCS(fcs, v);
1134 }
1135 printf("%x\n", fcs);
1136}
1137
1138/*
1139 * Check if this is a suitable place to put a breakpoint.
1140 */
1141static long check_bp_loc(unsigned long addr)
1142{
1143 unsigned int instr;
1144
1145 addr &= ~3;
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001146 if (!is_kernel_addr(addr)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 printf("Breakpoints may only be placed at kernel addresses\n");
1148 return 0;
1149 }
1150 if (!mread(addr, &instr, sizeof(instr))) {
1151 printf("Can't read instruction at address %lx\n", addr);
1152 return 0;
1153 }
1154 if (IS_MTMSRD(instr) || IS_RFID(instr)) {
1155 printf("Breakpoints may not be placed on mtmsrd or rfid "
1156 "instructions\n");
1157 return 0;
1158 }
1159 return 1;
1160}
1161
Michael Ellermane3bc8042012-08-23 22:09:13 +00001162static char *breakpoint_help_string =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 "Breakpoint command usage:\n"
1164 "b show breakpoints\n"
1165 "b <addr> [cnt] set breakpoint at given instr addr\n"
1166 "bc clear all breakpoints\n"
1167 "bc <n/addr> clear breakpoint number n or at addr\n"
Anshuman Khandual1ad7d702014-11-28 10:06:42 +05301168 "bi <addr> [cnt] set hardware instr breakpoint (POWER8 only)\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 "bd <addr> [cnt] set hardware data breakpoint\n"
1170 "";
1171
1172static void
1173bpt_cmds(void)
1174{
1175 int cmd;
1176 unsigned long a;
1177 int mode, i;
1178 struct bpt *bp;
1179 const char badaddr[] = "Only kernel addresses are permitted "
1180 "for breakpoints\n";
1181
1182 cmd = inchar();
1183 switch (cmd) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001184#ifndef CONFIG_8xx
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 case 'd': /* bd - hardware data breakpoint */
1186 mode = 7;
1187 cmd = inchar();
1188 if (cmd == 'r')
1189 mode = 5;
1190 else if (cmd == 'w')
1191 mode = 6;
1192 else
1193 termch = cmd;
1194 dabr.address = 0;
1195 dabr.enabled = 0;
1196 if (scanhex(&dabr.address)) {
Michael Ellerman51fae6de2005-12-04 18:39:15 +11001197 if (!is_kernel_addr(dabr.address)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 printf(badaddr);
1199 break;
1200 }
Michael Neuling9422de32012-12-20 14:06:44 +00001201 dabr.address &= ~HW_BRK_TYPE_DABR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 dabr.enabled = mode | BP_DABR;
1203 }
1204 break;
1205
1206 case 'i': /* bi - hardware instr breakpoint */
Anshuman Khandual1ad7d702014-11-28 10:06:42 +05301207 if (!cpu_has_feature(CPU_FTR_ARCH_207S)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 printf("Hardware instruction breakpoint "
1209 "not supported on this cpu\n");
1210 break;
1211 }
1212 if (iabr) {
Michael Ellermanabb90ee2014-12-01 16:54:13 +11001213 iabr->enabled &= ~BP_CIABR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 iabr = NULL;
1215 }
1216 if (!scanhex(&a))
1217 break;
1218 if (!check_bp_loc(a))
1219 break;
1220 bp = new_breakpoint(a);
1221 if (bp != NULL) {
Michael Ellermanabb90ee2014-12-01 16:54:13 +11001222 bp->enabled |= BP_CIABR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 iabr = bp;
1224 }
1225 break;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001226#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227
1228 case 'c':
1229 if (!scanhex(&a)) {
1230 /* clear all breakpoints */
1231 for (i = 0; i < NBPTS; ++i)
1232 bpts[i].enabled = 0;
1233 iabr = NULL;
1234 dabr.enabled = 0;
1235 printf("All breakpoints cleared\n");
1236 break;
1237 }
1238
1239 if (a <= NBPTS && a >= 1) {
1240 /* assume a breakpoint number */
1241 bp = &bpts[a-1]; /* bp nums are 1 based */
1242 } else {
1243 /* assume a breakpoint address */
1244 bp = at_breakpoint(a);
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001245 if (bp == NULL) {
Michael Ellerman736256e2014-05-26 21:02:14 +10001246 printf("No breakpoint at %lx\n", a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 break;
1248 }
1249 }
1250
Michael Ellerman736256e2014-05-26 21:02:14 +10001251 printf("Cleared breakpoint %lx (", BP_NUM(bp));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 xmon_print_symbol(bp->address, " ", ")\n");
1253 bp->enabled = 0;
1254 break;
1255
1256 default:
1257 termch = cmd;
Michael Ellermane3bc8042012-08-23 22:09:13 +00001258 cmd = skipbl();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 if (cmd == '?') {
1260 printf(breakpoint_help_string);
1261 break;
1262 }
1263 termch = cmd;
1264 if (!scanhex(&a)) {
1265 /* print all breakpoints */
1266 printf(" type address\n");
1267 if (dabr.enabled) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001268 printf(" data "REG" [", dabr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 if (dabr.enabled & 1)
1270 printf("r");
1271 if (dabr.enabled & 2)
1272 printf("w");
1273 printf("]\n");
1274 }
1275 for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
1276 if (!bp->enabled)
1277 continue;
1278 printf("%2x %s ", BP_NUM(bp),
Michael Ellermanabb90ee2014-12-01 16:54:13 +11001279 (bp->enabled & BP_CIABR) ? "inst": "trap");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 xmon_print_symbol(bp->address, " ", "\n");
1281 }
1282 break;
1283 }
1284
1285 if (!check_bp_loc(a))
1286 break;
1287 bp = new_breakpoint(a);
1288 if (bp != NULL)
1289 bp->enabled |= BP_TRAP;
1290 break;
1291 }
1292}
1293
1294/* Very cheap human name for vector lookup. */
1295static
1296const char *getvecname(unsigned long vec)
1297{
1298 char *ret;
1299
1300 switch (vec) {
1301 case 0x100: ret = "(System Reset)"; break;
1302 case 0x200: ret = "(Machine Check)"; break;
1303 case 0x300: ret = "(Data Access)"; break;
1304 case 0x380: ret = "(Data SLB Access)"; break;
1305 case 0x400: ret = "(Instruction Access)"; break;
1306 case 0x480: ret = "(Instruction SLB Access)"; break;
1307 case 0x500: ret = "(Hardware Interrupt)"; break;
1308 case 0x600: ret = "(Alignment)"; break;
1309 case 0x700: ret = "(Program Check)"; break;
1310 case 0x800: ret = "(FPU Unavailable)"; break;
1311 case 0x900: ret = "(Decrementer)"; break;
Michael Ellerman660e0342013-08-15 15:22:16 +10001312 case 0x980: ret = "(Hypervisor Decrementer)"; break;
1313 case 0xa00: ret = "(Doorbell)"; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 case 0xc00: ret = "(System Call)"; break;
1315 case 0xd00: ret = "(Single Step)"; break;
Michael Ellerman660e0342013-08-15 15:22:16 +10001316 case 0xe40: ret = "(Emulation Assist)"; break;
1317 case 0xe60: ret = "(HMI)"; break;
1318 case 0xe80: ret = "(Hypervisor Doorbell)"; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 case 0xf00: ret = "(Performance Monitor)"; break;
1320 case 0xf20: ret = "(Altivec Unavailable)"; break;
1321 case 0x1300: ret = "(Instruction Breakpoint)"; break;
Michael Ellerman660e0342013-08-15 15:22:16 +10001322 case 0x1500: ret = "(Denormalisation)"; break;
1323 case 0x1700: ret = "(Altivec Assist)"; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 default: ret = "";
1325 }
1326 return ret;
1327}
1328
1329static void get_function_bounds(unsigned long pc, unsigned long *startp,
1330 unsigned long *endp)
1331{
1332 unsigned long size, offset;
1333 const char *name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
1335 *startp = *endp = 0;
1336 if (pc == 0)
1337 return;
1338 if (setjmp(bus_error_jmp) == 0) {
1339 catch_memory_errors = 1;
1340 sync();
Alexey Dobriyanffb45122007-05-08 00:28:41 -07001341 name = kallsyms_lookup(pc, &size, &offset, NULL, tmpstr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 if (name != NULL) {
1343 *startp = pc - offset;
1344 *endp = pc - offset + size;
1345 }
1346 sync();
1347 }
1348 catch_memory_errors = 0;
1349}
1350
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001351#define LRSAVE_OFFSET (STACK_FRAME_LR_SAVE * sizeof(unsigned long))
1352#define MARKER_OFFSET (STACK_FRAME_MARKER * sizeof(unsigned long))
1353
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354static void xmon_show_stack(unsigned long sp, unsigned long lr,
1355 unsigned long pc)
1356{
Michael Ellerman0104cd62012-10-09 04:20:36 +00001357 int max_to_print = 64;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 unsigned long ip;
1359 unsigned long newsp;
1360 unsigned long marker;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 struct pt_regs regs;
1362
Michael Ellerman0104cd62012-10-09 04:20:36 +00001363 while (max_to_print--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 if (sp < PAGE_OFFSET) {
1365 if (sp != 0)
1366 printf("SP (%lx) is in userspace\n", sp);
1367 break;
1368 }
1369
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001370 if (!mread(sp + LRSAVE_OFFSET, &ip, sizeof(unsigned long))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 || !mread(sp, &newsp, sizeof(unsigned long))) {
1372 printf("Couldn't read stack frame at %lx\n", sp);
1373 break;
1374 }
1375
1376 /*
1377 * For the first stack frame, try to work out if
1378 * LR and/or the saved LR value in the bottommost
1379 * stack frame are valid.
1380 */
1381 if ((pc | lr) != 0) {
1382 unsigned long fnstart, fnend;
1383 unsigned long nextip;
1384 int printip = 1;
1385
1386 get_function_bounds(pc, &fnstart, &fnend);
1387 nextip = 0;
1388 if (newsp > sp)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001389 mread(newsp + LRSAVE_OFFSET, &nextip,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 sizeof(unsigned long));
1391 if (lr == ip) {
1392 if (lr < PAGE_OFFSET
1393 || (fnstart <= lr && lr < fnend))
1394 printip = 0;
1395 } else if (lr == nextip) {
1396 printip = 0;
1397 } else if (lr >= PAGE_OFFSET
1398 && !(fnstart <= lr && lr < fnend)) {
1399 printf("[link register ] ");
1400 xmon_print_symbol(lr, " ", "\n");
1401 }
1402 if (printip) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001403 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 xmon_print_symbol(ip, " ", " (unreliable)\n");
1405 }
1406 pc = lr = 0;
1407
1408 } else {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001409 printf("["REG"] ", sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 xmon_print_symbol(ip, " ", "\n");
1411 }
1412
1413 /* Look for "regshere" marker to see if this is
1414 an exception frame. */
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001415 if (mread(sp + MARKER_OFFSET, &marker, sizeof(unsigned long))
Benjamin Herrenschmidtec2b36b2008-04-17 14:34:59 +10001416 && marker == STACK_FRAME_REGS_MARKER) {
Michael Ellermanc4de3802012-10-09 04:20:35 +00001417 if (mread(sp + STACK_FRAME_OVERHEAD, &regs, sizeof(regs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 != sizeof(regs)) {
1419 printf("Couldn't read registers at %lx\n",
Michael Ellermanc4de3802012-10-09 04:20:35 +00001420 sp + STACK_FRAME_OVERHEAD);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 break;
1422 }
Michael Ellermane3bc8042012-08-23 22:09:13 +00001423 printf("--- Exception: %lx %s at ", regs.trap,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 getvecname(TRAP(&regs)));
1425 pc = regs.nip;
1426 lr = regs.link;
1427 xmon_print_symbol(pc, " ", "\n");
1428 }
1429
1430 if (newsp == 0)
1431 break;
1432
1433 sp = newsp;
Michael Ellerman0104cd62012-10-09 04:20:36 +00001434 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435}
1436
1437static void backtrace(struct pt_regs *excp)
1438{
1439 unsigned long sp;
1440
1441 if (scanhex(&sp))
1442 xmon_show_stack(sp, 0, 0);
1443 else
1444 xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
1445 scannl();
1446}
1447
1448static void print_bug_trap(struct pt_regs *regs)
1449{
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001450#ifdef CONFIG_BUG
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001451 const struct bug_entry *bug;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 unsigned long addr;
1453
1454 if (regs->msr & MSR_PR)
1455 return; /* not in kernel */
1456 addr = regs->nip; /* address of trap instruction */
1457 if (addr < PAGE_OFFSET)
1458 return;
1459 bug = find_bug(regs->nip);
1460 if (bug == NULL)
1461 return;
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001462 if (is_warning_bug(bug))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 return;
1464
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001465#ifdef CONFIG_DEBUG_BUGVERBOSE
Jeremy Fitzhardinge73c9cea2006-12-08 03:30:41 -08001466 printf("kernel BUG at %s:%u!\n",
1467 bug->file, bug->line);
Stephen Rothwell0a7c7ef2007-03-04 17:05:34 +11001468#else
1469 printf("kernel BUG at %p!\n", (void *)bug->bug_addr);
1470#endif
Paul Mackerrasebdba9a2008-10-31 21:34:09 +11001471#endif /* CONFIG_BUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472}
1473
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001474static void excprint(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475{
1476 unsigned long trap;
1477
1478#ifdef CONFIG_SMP
1479 printf("cpu 0x%x: ", smp_processor_id());
1480#endif /* CONFIG_SMP */
1481
1482 trap = TRAP(fp);
1483 printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(trap), fp);
1484 printf(" pc: ");
1485 xmon_print_symbol(fp->nip, ": ", "\n");
1486
1487 printf(" lr: ", fp->link);
1488 xmon_print_symbol(fp->link, ": ", "\n");
1489
1490 printf(" sp: %lx\n", fp->gpr[1]);
1491 printf(" msr: %lx\n", fp->msr);
1492
Aneesh Kumar K.Vce541522013-04-28 09:37:26 +00001493 if (trap == 0x300 || trap == 0x380 || trap == 0x600 || trap == 0x200) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 printf(" dar: %lx\n", fp->dar);
1495 if (trap != 0x380)
1496 printf(" dsisr: %lx\n", fp->dsisr);
1497 }
1498
1499 printf(" current = 0x%lx\n", current);
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001500#ifdef CONFIG_PPC64
Benjamin Herrenschmidt7230c562012-03-06 18:27:59 +11001501 printf(" paca = 0x%lx\t softe: %d\t irq_happened: 0x%02x\n",
1502 local_paca, local_paca->soft_enabled, local_paca->irq_happened);
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001503#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 if (current) {
1505 printf(" pid = %ld, comm = %s\n",
1506 current->pid, current->comm);
1507 }
1508
1509 if (trap == 0x700)
1510 print_bug_trap(fp);
1511}
1512
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001513static void prregs(struct pt_regs *fp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514{
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001515 int n, trap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 unsigned long base;
1517 struct pt_regs regs;
1518
1519 if (scanhex(&base)) {
1520 if (setjmp(bus_error_jmp) == 0) {
1521 catch_memory_errors = 1;
1522 sync();
1523 regs = *(struct pt_regs *)base;
1524 sync();
1525 __delay(200);
1526 } else {
1527 catch_memory_errors = 0;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001528 printf("*** Error reading registers from "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 base);
1530 return;
1531 }
1532 catch_memory_errors = 0;
1533 fp = &regs;
1534 }
1535
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001536#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 if (FULL_REGS(fp)) {
1538 for (n = 0; n < 16; ++n)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001539 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 n, fp->gpr[n], n+16, fp->gpr[n+16]);
1541 } else {
1542 for (n = 0; n < 7; ++n)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001543 printf("R%.2ld = "REG" R%.2ld = "REG"\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 n, fp->gpr[n], n+7, fp->gpr[n+7]);
1545 }
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001546#else
1547 for (n = 0; n < 32; ++n) {
1548 printf("R%.2d = %.8x%s", n, fp->gpr[n],
1549 (n & 3) == 3? "\n": " ");
1550 if (n == 12 && !FULL_REGS(fp)) {
1551 printf("\n");
1552 break;
1553 }
1554 }
1555#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 printf("pc = ");
1557 xmon_print_symbol(fp->nip, " ", "\n");
Paul Mackerras48404f22011-05-01 19:48:20 +00001558 if (TRAP(fp) != 0xc00 && cpu_has_feature(CPU_FTR_CFAR)) {
1559 printf("cfar= ");
1560 xmon_print_symbol(fp->orig_gpr3, " ", "\n");
1561 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 printf("lr = ");
1563 xmon_print_symbol(fp->link, " ", "\n");
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001564 printf("msr = "REG" cr = %.8lx\n", fp->msr, fp->ccr);
1565 printf("ctr = "REG" xer = "REG" trap = %4lx\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 fp->ctr, fp->xer, fp->trap);
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001567 trap = TRAP(fp);
1568 if (trap == 0x300 || trap == 0x380 || trap == 0x600)
1569 printf("dar = "REG" dsisr = %.8lx\n", fp->dar, fp->dsisr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570}
1571
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001572static void cacheflush(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573{
1574 int cmd;
1575 unsigned long nflush;
1576
1577 cmd = inchar();
1578 if (cmd != 'i')
1579 termch = cmd;
1580 scanhex((void *)&adrs);
1581 if (termch != '\n')
1582 termch = 0;
1583 nflush = 1;
1584 scanhex(&nflush);
1585 nflush = (nflush + L1_CACHE_BYTES - 1) / L1_CACHE_BYTES;
1586 if (setjmp(bus_error_jmp) == 0) {
1587 catch_memory_errors = 1;
1588 sync();
1589
1590 if (cmd != 'i') {
1591 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1592 cflush((void *) adrs);
1593 } else {
1594 for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
1595 cinval((void *) adrs);
1596 }
1597 sync();
1598 /* wait a little while to see if we get a machine check */
1599 __delay(200);
1600 }
1601 catch_memory_errors = 0;
1602}
1603
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001604static unsigned long
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605read_spr(int n)
1606{
1607 unsigned int instrs[2];
1608 unsigned long (*code)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 unsigned long ret = -1UL;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001610#ifdef CONFIG_PPC64
1611 unsigned long opd[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 opd[0] = (unsigned long)instrs;
1614 opd[1] = 0;
1615 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001616 code = (unsigned long (*)(void)) opd;
1617#else
1618 code = (unsigned long (*)(void)) instrs;
1619#endif
1620
1621 /* mfspr r3,n; blr */
1622 instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1623 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 store_inst(instrs);
1625 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626
1627 if (setjmp(bus_error_jmp) == 0) {
1628 catch_memory_errors = 1;
1629 sync();
1630
1631 ret = code();
1632
1633 sync();
1634 /* wait a little while to see if we get a machine check */
1635 __delay(200);
1636 n = size;
1637 }
1638
1639 return ret;
1640}
1641
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001642static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643write_spr(int n, unsigned long val)
1644{
1645 unsigned int instrs[2];
1646 unsigned long (*code)(unsigned long);
Paul Mackerras548cceb2005-11-11 22:36:34 +11001647#ifdef CONFIG_PPC64
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 unsigned long opd[3];
1649
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 opd[0] = (unsigned long)instrs;
1651 opd[1] = 0;
1652 opd[2] = 0;
Paul Mackerras548cceb2005-11-11 22:36:34 +11001653 code = (unsigned long (*)(unsigned long)) opd;
1654#else
1655 code = (unsigned long (*)(unsigned long)) instrs;
1656#endif
1657
1658 instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
1659 instrs[1] = 0x4e800020;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 store_inst(instrs);
1661 store_inst(instrs+1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662
1663 if (setjmp(bus_error_jmp) == 0) {
1664 catch_memory_errors = 1;
1665 sync();
1666
1667 code(val);
1668
1669 sync();
1670 /* wait a little while to see if we get a machine check */
1671 __delay(200);
1672 n = size;
1673 }
1674}
1675
1676static unsigned long regno;
1677extern char exc_prolog;
1678extern char dec_exc;
1679
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001680static void super_regs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681{
1682 int cmd;
1683 unsigned long val;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
1685 cmd = skipbl();
1686 if (cmd == '\n') {
Michael Ellermane3bc8042012-08-23 22:09:13 +00001687 unsigned long sp, toc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 asm("mr %0,1" : "=r" (sp) :);
1689 asm("mr %0,2" : "=r" (toc) :);
1690
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001691 printf("msr = "REG" sprg0= "REG"\n",
1692 mfmsr(), mfspr(SPRN_SPRG0));
1693 printf("pvr = "REG" sprg1= "REG"\n",
Michael Ellermane3bc8042012-08-23 22:09:13 +00001694 mfspr(SPRN_PVR), mfspr(SPRN_SPRG1));
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001695 printf("dec = "REG" sprg2= "REG"\n",
1696 mfspr(SPRN_DEC), mfspr(SPRN_SPRG2));
1697 printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3));
1698 printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699
1700 return;
1701 }
1702
1703 scanhex(&regno);
1704 switch (cmd) {
1705 case 'w':
1706 val = read_spr(regno);
1707 scanhex(&val);
1708 write_spr(regno, val);
1709 /* fall through */
1710 case 'r':
1711 printf("spr %lx = %lx\n", regno, read_spr(regno));
1712 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 }
1714 scannl();
1715}
1716
1717/*
1718 * Stuff for reading and writing memory safely
1719 */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001720static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721mread(unsigned long adrs, void *buf, int size)
1722{
1723 volatile int n;
1724 char *p, *q;
1725
1726 n = 0;
1727 if (setjmp(bus_error_jmp) == 0) {
1728 catch_memory_errors = 1;
1729 sync();
1730 p = (char *)adrs;
1731 q = (char *)buf;
1732 switch (size) {
1733 case 2:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001734 *(u16 *)q = *(u16 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 break;
1736 case 4:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001737 *(u32 *)q = *(u32 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 break;
1739 case 8:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001740 *(u64 *)q = *(u64 *)p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 break;
1742 default:
1743 for( ; n < size; ++n) {
1744 *q++ = *p++;
1745 sync();
1746 }
1747 }
1748 sync();
1749 /* wait a little while to see if we get a machine check */
1750 __delay(200);
1751 n = size;
1752 }
1753 catch_memory_errors = 0;
1754 return n;
1755}
1756
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001757static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758mwrite(unsigned long adrs, void *buf, int size)
1759{
1760 volatile int n;
1761 char *p, *q;
1762
1763 n = 0;
1764 if (setjmp(bus_error_jmp) == 0) {
1765 catch_memory_errors = 1;
1766 sync();
1767 p = (char *) adrs;
1768 q = (char *) buf;
1769 switch (size) {
1770 case 2:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001771 *(u16 *)p = *(u16 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 break;
1773 case 4:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001774 *(u32 *)p = *(u32 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775 break;
1776 case 8:
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001777 *(u64 *)p = *(u64 *)q;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 break;
1779 default:
1780 for ( ; n < size; ++n) {
1781 *p++ = *q++;
1782 sync();
1783 }
1784 }
1785 sync();
1786 /* wait a little while to see if we get a machine check */
1787 __delay(200);
1788 n = size;
1789 } else {
Michael Ellerman736256e2014-05-26 21:02:14 +10001790 printf("*** Error writing address "REG"\n", adrs + n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 }
1792 catch_memory_errors = 0;
1793 return n;
1794}
1795
1796static int fault_type;
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001797static int fault_except;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798static char *fault_chars[] = { "--", "**", "##" };
1799
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001800static int handle_fault(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801{
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10001802 fault_except = TRAP(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803 switch (TRAP(regs)) {
1804 case 0x200:
1805 fault_type = 0;
1806 break;
1807 case 0x300:
1808 case 0x380:
1809 fault_type = 1;
1810 break;
1811 default:
1812 fault_type = 2;
1813 }
1814
1815 longjmp(bus_error_jmp, 1);
1816
1817 return 0;
1818}
1819
1820#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
1821
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001822static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823byterev(unsigned char *val, int size)
1824{
1825 int t;
1826
1827 switch (size) {
1828 case 2:
1829 SWAP(val[0], val[1], t);
1830 break;
1831 case 4:
1832 SWAP(val[0], val[3], t);
1833 SWAP(val[1], val[2], t);
1834 break;
1835 case 8: /* is there really any use for this? */
1836 SWAP(val[0], val[7], t);
1837 SWAP(val[1], val[6], t);
1838 SWAP(val[2], val[5], t);
1839 SWAP(val[3], val[4], t);
1840 break;
1841 }
1842}
1843
1844static int brev;
1845static int mnoread;
1846
Michael Ellermane3bc8042012-08-23 22:09:13 +00001847static char *memex_help_string =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848 "Memory examine command usage:\n"
1849 "m [addr] [flags] examine/change memory\n"
1850 " addr is optional. will start where left off.\n"
1851 " flags may include chars from this set:\n"
1852 " b modify by bytes (default)\n"
1853 " w modify by words (2 byte)\n"
1854 " l modify by longs (4 byte)\n"
1855 " d modify by doubleword (8 byte)\n"
1856 " r toggle reverse byte order mode\n"
1857 " n do not read memory (for i/o spaces)\n"
1858 " . ok to read (default)\n"
1859 "NOTE: flags are saved as defaults\n"
1860 "";
1861
Michael Ellermane3bc8042012-08-23 22:09:13 +00001862static char *memex_subcmd_help_string =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863 "Memory examine subcommands:\n"
1864 " hexval write this val to current location\n"
1865 " 'string' write chars from string to this location\n"
1866 " ' increment address\n"
1867 " ^ decrement address\n"
1868 " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n"
1869 " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n"
1870 " ` clear no-read flag\n"
1871 " ; stay at this addr\n"
1872 " v change to byte mode\n"
1873 " w change to word (2 byte) mode\n"
1874 " l change to long (4 byte) mode\n"
1875 " u change to doubleword (8 byte) mode\n"
1876 " m addr change current addr\n"
1877 " n toggle no-read flag\n"
1878 " r toggle byte reverse flag\n"
1879 " < count back up count bytes\n"
1880 " > count skip forward count bytes\n"
1881 " x exit this mode\n"
1882 "";
1883
Michael Ellerman9f1067c22008-05-08 14:27:16 +10001884static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885memex(void)
1886{
1887 int cmd, inc, i, nslash;
1888 unsigned long n;
1889 unsigned char val[16];
1890
1891 scanhex((void *)&adrs);
1892 cmd = skipbl();
1893 if (cmd == '?') {
1894 printf(memex_help_string);
1895 return;
1896 } else {
1897 termch = cmd;
1898 }
1899 last_cmd = "m\n";
1900 while ((cmd = skipbl()) != '\n') {
1901 switch( cmd ){
1902 case 'b': size = 1; break;
1903 case 'w': size = 2; break;
1904 case 'l': size = 4; break;
1905 case 'd': size = 8; break;
1906 case 'r': brev = !brev; break;
1907 case 'n': mnoread = 1; break;
1908 case '.': mnoread = 0; break;
1909 }
1910 }
1911 if( size <= 0 )
1912 size = 1;
1913 else if( size > 8 )
1914 size = 8;
1915 for(;;){
1916 if (!mnoread)
1917 n = mread(adrs, val, size);
Paul Mackerrase1449ed2005-11-10 14:30:20 +11001918 printf(REG"%c", adrs, brev? 'r': ' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 if (!mnoread) {
1920 if (brev)
1921 byterev(val, size);
1922 putchar(' ');
1923 for (i = 0; i < n; ++i)
1924 printf("%.2x", val[i]);
1925 for (; i < size; ++i)
1926 printf("%s", fault_chars[fault_type]);
1927 }
1928 putchar(' ');
1929 inc = size;
1930 nslash = 0;
1931 for(;;){
1932 if( scanhex(&n) ){
1933 for (i = 0; i < size; ++i)
1934 val[i] = n >> (i * 8);
1935 if (!brev)
1936 byterev(val, size);
1937 mwrite(adrs, val, size);
1938 inc = size;
1939 }
1940 cmd = skipbl();
1941 if (cmd == '\n')
1942 break;
1943 inc = 0;
1944 switch (cmd) {
1945 case '\'':
1946 for(;;){
1947 n = inchar();
1948 if( n == '\\' )
1949 n = bsesc();
1950 else if( n == '\'' )
1951 break;
1952 for (i = 0; i < size; ++i)
1953 val[i] = n >> (i * 8);
1954 if (!brev)
1955 byterev(val, size);
1956 mwrite(adrs, val, size);
1957 adrs += size;
1958 }
1959 adrs -= size;
1960 inc = size;
1961 break;
1962 case ',':
1963 adrs += size;
1964 break;
1965 case '.':
1966 mnoread = 0;
1967 break;
1968 case ';':
1969 break;
1970 case 'x':
1971 case EOF:
1972 scannl();
1973 return;
1974 case 'b':
1975 case 'v':
1976 size = 1;
1977 break;
1978 case 'w':
1979 size = 2;
1980 break;
1981 case 'l':
1982 size = 4;
1983 break;
1984 case 'u':
1985 size = 8;
1986 break;
1987 case '^':
1988 adrs -= size;
1989 break;
1990 break;
1991 case '/':
1992 if (nslash > 0)
1993 adrs -= 1 << nslash;
1994 else
1995 nslash = 0;
1996 nslash += 4;
1997 adrs += 1 << nslash;
1998 break;
1999 case '\\':
2000 if (nslash < 0)
2001 adrs += 1 << -nslash;
2002 else
2003 nslash = 0;
2004 nslash -= 4;
2005 adrs -= 1 << -nslash;
2006 break;
2007 case 'm':
2008 scanhex((void *)&adrs);
2009 break;
2010 case 'n':
2011 mnoread = 1;
2012 break;
2013 case 'r':
2014 brev = !brev;
2015 break;
2016 case '<':
2017 n = size;
2018 scanhex(&n);
2019 adrs -= n;
2020 break;
2021 case '>':
2022 n = size;
2023 scanhex(&n);
2024 adrs += n;
2025 break;
2026 case '?':
2027 printf(memex_subcmd_help_string);
2028 break;
2029 }
2030 }
2031 adrs += inc;
2032 }
2033}
2034
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002035static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036bsesc(void)
2037{
2038 int c;
2039
2040 c = inchar();
2041 switch( c ){
2042 case 'n': c = '\n'; break;
2043 case 'r': c = '\r'; break;
2044 case 'b': c = '\b'; break;
2045 case 't': c = '\t'; break;
2046 }
2047 return c;
2048}
2049
Olaf Hering7e5b5932006-03-08 20:40:28 +01002050static void xmon_rawdump (unsigned long adrs, long ndump)
2051{
2052 long n, m, r, nr;
2053 unsigned char temp[16];
2054
2055 for (n = ndump; n > 0;) {
2056 r = n < 16? n: 16;
2057 nr = mread(adrs, temp, r);
2058 adrs += nr;
2059 for (m = 0; m < r; ++m) {
2060 if (m < nr)
2061 printf("%.2x", temp[m]);
2062 else
2063 printf("%s", fault_chars[fault_type]);
2064 }
2065 n -= r;
2066 if (nr < r)
2067 break;
2068 }
2069 printf("\n");
2070}
2071
Michael Ellermanddadb6b2012-09-13 23:01:31 +00002072#ifdef CONFIG_PPC64
2073static void dump_one_paca(int cpu)
2074{
2075 struct paca_struct *p;
2076
2077 if (setjmp(bus_error_jmp) != 0) {
2078 printf("*** Error dumping paca for cpu 0x%x!\n", cpu);
2079 return;
2080 }
2081
2082 catch_memory_errors = 1;
2083 sync();
2084
2085 p = &paca[cpu];
2086
2087 printf("paca for cpu 0x%x @ %p:\n", cpu, p);
2088
2089 printf(" %-*s = %s\n", 16, "possible", cpu_possible(cpu) ? "yes" : "no");
2090 printf(" %-*s = %s\n", 16, "present", cpu_present(cpu) ? "yes" : "no");
2091 printf(" %-*s = %s\n", 16, "online", cpu_online(cpu) ? "yes" : "no");
2092
2093#define DUMP(paca, name, format) \
2094 printf(" %-*s = %#-*"format"\t(0x%lx)\n", 16, #name, 18, paca->name, \
2095 offsetof(struct paca_struct, name));
2096
2097 DUMP(p, lock_token, "x");
2098 DUMP(p, paca_index, "x");
2099 DUMP(p, kernel_toc, "lx");
2100 DUMP(p, kernelbase, "lx");
2101 DUMP(p, kernel_msr, "lx");
Michael Ellermanddadb6b2012-09-13 23:01:31 +00002102 DUMP(p, emergency_sp, "p");
Mahesh Salgaonkar729b0f72013-10-30 20:04:00 +05302103#ifdef CONFIG_PPC_BOOK3S_64
2104 DUMP(p, mc_emergency_sp, "p");
2105 DUMP(p, in_mce, "x");
2106#endif
Michael Ellermanddadb6b2012-09-13 23:01:31 +00002107 DUMP(p, data_offset, "lx");
2108 DUMP(p, hw_cpu_id, "x");
2109 DUMP(p, cpu_start, "x");
2110 DUMP(p, kexec_state, "x");
2111 DUMP(p, __current, "p");
2112 DUMP(p, kstack, "lx");
2113 DUMP(p, stab_rr, "lx");
2114 DUMP(p, saved_r1, "lx");
2115 DUMP(p, trap_save, "x");
2116 DUMP(p, soft_enabled, "x");
2117 DUMP(p, irq_happened, "x");
2118 DUMP(p, io_sync, "x");
2119 DUMP(p, irq_work_pending, "x");
2120 DUMP(p, nap_state_lost, "x");
2121
2122#undef DUMP
2123
2124 catch_memory_errors = 0;
2125 sync();
2126}
2127
2128static void dump_all_pacas(void)
2129{
2130 int cpu;
2131
2132 if (num_possible_cpus() == 0) {
2133 printf("No possible cpus, use 'dp #' to dump individual cpus\n");
2134 return;
2135 }
2136
2137 for_each_possible_cpu(cpu)
2138 dump_one_paca(cpu);
2139}
2140
2141static void dump_pacas(void)
2142{
2143 unsigned long num;
2144 int c;
2145
2146 c = inchar();
2147 if (c == 'a') {
2148 dump_all_pacas();
2149 return;
2150 }
2151
2152 termch = c; /* Put c back, it wasn't 'a' */
2153
2154 if (scanhex(&num))
2155 dump_one_paca(num);
2156 else
2157 dump_one_paca(xmon_owner);
2158}
2159#endif
2160
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002161static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162dump(void)
2163{
2164 int c;
2165
2166 c = inchar();
Michael Ellermanddadb6b2012-09-13 23:01:31 +00002167
2168#ifdef CONFIG_PPC64
2169 if (c == 'p') {
2170 dump_pacas();
2171 return;
2172 }
2173#endif
2174
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n')
2176 termch = c;
2177 scanhex((void *)&adrs);
2178 if (termch != '\n')
2179 termch = 0;
2180 if (c == 'i') {
2181 scanhex(&nidump);
2182 if (nidump == 0)
2183 nidump = 16;
2184 else if (nidump > MAX_DUMP)
2185 nidump = MAX_DUMP;
2186 adrs += ppc_inst_dump(adrs, nidump, 1);
2187 last_cmd = "di\n";
Vinay Sridharf312deb2009-05-14 23:13:07 +00002188 } else if (c == 'l') {
2189 dump_log_buf();
Olaf Hering7e5b5932006-03-08 20:40:28 +01002190 } else if (c == 'r') {
2191 scanhex(&ndump);
2192 if (ndump == 0)
2193 ndump = 64;
2194 xmon_rawdump(adrs, ndump);
2195 adrs += ndump;
2196 last_cmd = "dr\n";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197 } else {
2198 scanhex(&ndump);
2199 if (ndump == 0)
2200 ndump = 64;
2201 else if (ndump > MAX_DUMP)
2202 ndump = MAX_DUMP;
2203 prdump(adrs, ndump);
2204 adrs += ndump;
2205 last_cmd = "d\n";
2206 }
2207}
2208
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002209static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210prdump(unsigned long adrs, long ndump)
2211{
2212 long n, m, c, r, nr;
2213 unsigned char temp[16];
2214
2215 for (n = ndump; n > 0;) {
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002216 printf(REG, adrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217 putchar(' ');
2218 r = n < 16? n: 16;
2219 nr = mread(adrs, temp, r);
2220 adrs += nr;
2221 for (m = 0; m < r; ++m) {
Michael Ellermane3bc8042012-08-23 22:09:13 +00002222 if ((m & (sizeof(long) - 1)) == 0 && m > 0)
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002223 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 if (m < nr)
2225 printf("%.2x", temp[m]);
2226 else
2227 printf("%s", fault_chars[fault_type]);
2228 }
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002229 for (; m < 16; ++m) {
Michael Ellermane3bc8042012-08-23 22:09:13 +00002230 if ((m & (sizeof(long) - 1)) == 0)
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002231 putchar(' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 printf(" ");
Paul Mackerrase1449ed2005-11-10 14:30:20 +11002233 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 printf(" |");
2235 for (m = 0; m < r; ++m) {
2236 if (m < nr) {
2237 c = temp[m];
2238 putchar(' ' <= c && c <= '~'? c: '.');
2239 } else
2240 putchar(' ');
2241 }
2242 n -= r;
2243 for (; m < 16; ++m)
2244 putchar(' ');
2245 printf("|\n");
2246 if (nr < r)
2247 break;
2248 }
2249}
2250
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002251typedef int (*instruction_dump_func)(unsigned long inst, unsigned long addr);
2252
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002253static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002254generic_inst_dump(unsigned long adr, long count, int praddr,
2255 instruction_dump_func dump_func)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256{
2257 int nr, dotted;
2258 unsigned long first_adr;
2259 unsigned long inst, last_inst = 0;
2260 unsigned char val[4];
2261
2262 dotted = 0;
2263 for (first_adr = adr; count > 0; --count, adr += 4) {
2264 nr = mread(adr, val, 4);
2265 if (nr == 0) {
2266 if (praddr) {
2267 const char *x = fault_chars[fault_type];
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002268 printf(REG" %s%s%s%s\n", adr, x, x, x, x);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 }
2270 break;
2271 }
2272 inst = GETWORD(val);
2273 if (adr > first_adr && inst == last_inst) {
2274 if (!dotted) {
2275 printf(" ...\n");
2276 dotted = 1;
2277 }
2278 continue;
2279 }
2280 dotted = 0;
2281 last_inst = inst;
2282 if (praddr)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002283 printf(REG" %.8x", adr, inst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 printf("\t");
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002285 dump_func(inst, adr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 printf("\n");
2287 }
2288 return adr - first_adr;
2289}
2290
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002291static int
Michael Ellerman4c4c8722006-11-23 00:46:42 +01002292ppc_inst_dump(unsigned long adr, long count, int praddr)
2293{
2294 return generic_inst_dump(adr, count, praddr, print_insn_powerpc);
2295}
2296
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297void
2298print_address(unsigned long addr)
2299{
2300 xmon_print_symbol(addr, "\t# ", "");
2301}
2302
Vinay Sridharf312deb2009-05-14 23:13:07 +00002303void
2304dump_log_buf(void)
2305{
Michael Ellermanca5dd392012-08-23 22:09:12 +00002306 struct kmsg_dumper dumper = { .active = 1 };
2307 unsigned char buf[128];
2308 size_t len;
Vinay Sridharf312deb2009-05-14 23:13:07 +00002309
Michael Ellermane3bc8042012-08-23 22:09:13 +00002310 if (setjmp(bus_error_jmp) != 0) {
Michael Ellermanca5dd392012-08-23 22:09:12 +00002311 printf("Error dumping printk buffer!\n");
Michael Ellermane3bc8042012-08-23 22:09:13 +00002312 return;
2313 }
Vinay Sridharf312deb2009-05-14 23:13:07 +00002314
Michael Ellermane3bc8042012-08-23 22:09:13 +00002315 catch_memory_errors = 1;
2316 sync();
Vinay Sridharf312deb2009-05-14 23:13:07 +00002317
Michael Ellermanca5dd392012-08-23 22:09:12 +00002318 kmsg_dump_rewind_nolock(&dumper);
2319 while (kmsg_dump_get_line_nolock(&dumper, false, buf, sizeof(buf), &len)) {
2320 buf[len] = '\0';
2321 printf("%s", buf);
2322 }
Vinay Sridharf312deb2009-05-14 23:13:07 +00002323
Michael Ellermane3bc8042012-08-23 22:09:13 +00002324 sync();
2325 /* wait a little while to see if we get a machine check */
2326 __delay(200);
2327 catch_memory_errors = 0;
Vinay Sridharf312deb2009-05-14 23:13:07 +00002328}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329
2330/*
2331 * Memory operations - move, set, print differences
2332 */
2333static unsigned long mdest; /* destination address */
2334static unsigned long msrc; /* source address */
2335static unsigned long mval; /* byte value to set memory to */
2336static unsigned long mcount; /* # bytes to affect */
2337static unsigned long mdiffs; /* max # differences to print */
2338
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002339static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340memops(int cmd)
2341{
2342 scanhex((void *)&mdest);
2343 if( termch != '\n' )
2344 termch = 0;
2345 scanhex((void *)(cmd == 's'? &mval: &msrc));
2346 if( termch != '\n' )
2347 termch = 0;
2348 scanhex((void *)&mcount);
2349 switch( cmd ){
2350 case 'm':
2351 memmove((void *)mdest, (void *)msrc, mcount);
2352 break;
2353 case 's':
2354 memset((void *)mdest, mval, mcount);
2355 break;
2356 case 'd':
2357 if( termch != '\n' )
2358 termch = 0;
2359 scanhex((void *)&mdiffs);
2360 memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs);
2361 break;
2362 }
2363}
2364
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002365static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr)
2367{
2368 unsigned n, prt;
2369
2370 prt = 0;
2371 for( n = nb; n > 0; --n )
2372 if( *p1++ != *p2++ )
2373 if( ++prt <= maxpr )
2374 printf("%.16x %.2x # %.16x %.2x\n", p1 - 1,
2375 p1[-1], p2 - 1, p2[-1]);
2376 if( prt > maxpr )
2377 printf("Total of %d differences\n", prt);
2378}
2379
2380static unsigned mend;
2381static unsigned mask;
2382
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002383static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384memlocate(void)
2385{
2386 unsigned a, n;
2387 unsigned char val[4];
2388
2389 last_cmd = "ml";
2390 scanhex((void *)&mdest);
2391 if (termch != '\n') {
2392 termch = 0;
2393 scanhex((void *)&mend);
2394 if (termch != '\n') {
2395 termch = 0;
2396 scanhex((void *)&mval);
2397 mask = ~0;
2398 if (termch != '\n') termch = 0;
2399 scanhex((void *)&mask);
2400 }
2401 }
2402 n = 0;
2403 for (a = mdest; a < mend; a += 4) {
2404 if (mread(a, val, 4) == 4
2405 && ((GETWORD(val) ^ mval) & mask) == 0) {
2406 printf("%.16x: %.16x\n", a, GETWORD(val));
2407 if (++n >= 10)
2408 break;
2409 }
2410 }
2411}
2412
2413static unsigned long mskip = 0x1000;
2414static unsigned long mlim = 0xffffffff;
2415
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002416static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417memzcan(void)
2418{
2419 unsigned char v;
2420 unsigned a;
2421 int ok, ook;
2422
2423 scanhex(&mdest);
2424 if (termch != '\n') termch = 0;
2425 scanhex(&mskip);
2426 if (termch != '\n') termch = 0;
2427 scanhex(&mlim);
2428 ook = 0;
2429 for (a = mdest; a < mlim; a += mskip) {
2430 ok = mread(a, &v, 1);
2431 if (ok && !ook) {
2432 printf("%.8x .. ", a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433 } else if (!ok && ook)
2434 printf("%.8x\n", a - mskip);
2435 ook = ok;
2436 if (a + mskip < a)
2437 break;
2438 }
2439 if (ook)
2440 printf("%.8x\n", a - mskip);
2441}
2442
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002443static void proccall(void)
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002444{
2445 unsigned long args[8];
2446 unsigned long ret;
2447 int i;
2448 typedef unsigned long (*callfunc_t)(unsigned long, unsigned long,
2449 unsigned long, unsigned long, unsigned long,
2450 unsigned long, unsigned long, unsigned long);
2451 callfunc_t func;
2452
2453 if (!scanhex(&adrs))
2454 return;
2455 if (termch != '\n')
2456 termch = 0;
2457 for (i = 0; i < 8; ++i)
2458 args[i] = 0;
2459 for (i = 0; i < 8; ++i) {
2460 if (!scanhex(&args[i]) || termch == '\n')
2461 break;
2462 termch = 0;
2463 }
2464 func = (callfunc_t) adrs;
2465 ret = 0;
2466 if (setjmp(bus_error_jmp) == 0) {
2467 catch_memory_errors = 1;
2468 sync();
2469 ret = func(args[0], args[1], args[2], args[3],
2470 args[4], args[5], args[6], args[7]);
2471 sync();
Michael Ellerman736256e2014-05-26 21:02:14 +10002472 printf("return value is 0x%lx\n", ret);
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002473 } else {
2474 printf("*** %x exception occurred\n", fault_except);
2475 }
2476 catch_memory_errors = 0;
2477}
2478
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479/* Input scanning routines */
2480int
2481skipbl(void)
2482{
2483 int c;
2484
2485 if( termch != 0 ){
2486 c = termch;
2487 termch = 0;
2488 } else
2489 c = inchar();
2490 while( c == ' ' || c == '\t' )
2491 c = inchar();
2492 return c;
2493}
2494
2495#define N_PTREGS 44
2496static char *regnames[N_PTREGS] = {
2497 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
2498 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
2499 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
2500 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002501 "pc", "msr", "or3", "ctr", "lr", "xer", "ccr",
2502#ifdef CONFIG_PPC64
2503 "softe",
2504#else
2505 "mq",
2506#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 "trap", "dar", "dsisr", "res"
2508};
2509
2510int
2511scanhex(unsigned long *vp)
2512{
2513 int c, d;
2514 unsigned long v;
2515
2516 c = skipbl();
2517 if (c == '%') {
2518 /* parse register name */
2519 char regname[8];
2520 int i;
2521
2522 for (i = 0; i < sizeof(regname) - 1; ++i) {
2523 c = inchar();
2524 if (!isalnum(c)) {
2525 termch = c;
2526 break;
2527 }
2528 regname[i] = c;
2529 }
2530 regname[i] = 0;
2531 for (i = 0; i < N_PTREGS; ++i) {
2532 if (strcmp(regnames[i], regname) == 0) {
2533 if (xmon_regs == NULL) {
2534 printf("regs not available\n");
2535 return 0;
2536 }
2537 *vp = ((unsigned long *)xmon_regs)[i];
2538 return 1;
2539 }
2540 }
2541 printf("invalid register name '%%%s'\n", regname);
2542 return 0;
2543 }
2544
2545 /* skip leading "0x" if any */
2546
2547 if (c == '0') {
2548 c = inchar();
2549 if (c == 'x') {
2550 c = inchar();
2551 } else {
2552 d = hexdigit(c);
2553 if (d == EOF) {
2554 termch = c;
2555 *vp = 0;
2556 return 1;
2557 }
2558 }
2559 } else if (c == '$') {
2560 int i;
2561 for (i=0; i<63; i++) {
2562 c = inchar();
Vincent Bernat05b981f2014-07-15 13:43:47 +02002563 if (isspace(c) || c == '\0') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564 termch = c;
2565 break;
2566 }
2567 tmpstr[i] = c;
2568 }
2569 tmpstr[i++] = 0;
Benjamin Herrenschmidt6879dc12005-06-21 17:15:30 -07002570 *vp = 0;
2571 if (setjmp(bus_error_jmp) == 0) {
2572 catch_memory_errors = 1;
2573 sync();
2574 *vp = kallsyms_lookup_name(tmpstr);
2575 sync();
2576 }
2577 catch_memory_errors = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 if (!(*vp)) {
2579 printf("unknown symbol '%s'\n", tmpstr);
2580 return 0;
2581 }
2582 return 1;
2583 }
2584
2585 d = hexdigit(c);
2586 if (d == EOF) {
2587 termch = c;
2588 return 0;
2589 }
2590 v = 0;
2591 do {
2592 v = (v << 4) + d;
2593 c = inchar();
2594 d = hexdigit(c);
2595 } while (d != EOF);
2596 termch = c;
2597 *vp = v;
2598 return 1;
2599}
2600
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002601static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602scannl(void)
2603{
2604 int c;
2605
2606 c = termch;
2607 termch = 0;
2608 while( c != '\n' )
2609 c = inchar();
2610}
2611
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002612static int hexdigit(int c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613{
2614 if( '0' <= c && c <= '9' )
2615 return c - '0';
2616 if( 'A' <= c && c <= 'F' )
2617 return c - ('A' - 10);
2618 if( 'a' <= c && c <= 'f' )
2619 return c - ('a' - 10);
2620 return EOF;
2621}
2622
2623void
2624getstring(char *s, int size)
2625{
2626 int c;
2627
2628 c = skipbl();
2629 do {
2630 if( size > 1 ){
2631 *s++ = c;
2632 --size;
2633 }
2634 c = inchar();
2635 } while( c != ' ' && c != '\t' && c != '\n' );
2636 termch = c;
2637 *s = 0;
2638}
2639
2640static char line[256];
2641static char *lineptr;
2642
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002643static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644flush_input(void)
2645{
2646 lineptr = NULL;
2647}
2648
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002649static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650inchar(void)
2651{
2652 if (lineptr == NULL || *lineptr == 0) {
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002653 if (xmon_gets(line, sizeof(line)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654 lineptr = NULL;
2655 return EOF;
2656 }
2657 lineptr = line;
2658 }
2659 return *lineptr++;
2660}
2661
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002662static void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663take_input(char *str)
2664{
2665 lineptr = str;
2666}
2667
2668
2669static void
2670symbol_lookup(void)
2671{
2672 int type = inchar();
2673 unsigned long addr;
2674 static char tmp[64];
2675
2676 switch (type) {
2677 case 'a':
2678 if (scanhex(&addr))
2679 xmon_print_symbol(addr, ": ", "\n");
2680 termch = 0;
2681 break;
2682 case 's':
2683 getstring(tmp, 64);
2684 if (setjmp(bus_error_jmp) == 0) {
2685 catch_memory_errors = 1;
2686 sync();
2687 addr = kallsyms_lookup_name(tmp);
2688 if (addr)
2689 printf("%s: %lx\n", tmp, addr);
2690 else
2691 printf("Symbol '%s' not found.\n", tmp);
2692 sync();
2693 }
2694 catch_memory_errors = 0;
2695 termch = 0;
2696 break;
2697 }
2698}
2699
2700
2701/* Print an address in numeric and symbolic form (if possible) */
2702static void xmon_print_symbol(unsigned long address, const char *mid,
2703 const char *after)
2704{
2705 char *modname;
2706 const char *name = NULL;
2707 unsigned long offset, size;
2708
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002709 printf(REG, address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710 if (setjmp(bus_error_jmp) == 0) {
2711 catch_memory_errors = 1;
2712 sync();
2713 name = kallsyms_lookup(address, &size, &offset, &modname,
2714 tmpstr);
2715 sync();
2716 /* wait a little while to see if we get a machine check */
2717 __delay(200);
2718 }
2719
2720 catch_memory_errors = 0;
2721
2722 if (name) {
2723 printf("%s%s+%#lx/%#lx", mid, name, offset, size);
2724 if (modname)
2725 printf(" [%s]", modname);
2726 }
2727 printf("%s", after);
2728}
2729
Benjamin Herrenschmidt2d27cfd2009-07-23 23:15:59 +00002730#ifdef CONFIG_PPC_BOOK3S_64
Michael Ellerman13b3d132014-07-10 12:29:20 +10002731void dump_segments(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732{
2733 int i;
will schmidtb3b95952007-12-07 08:22:23 +11002734 unsigned long esid,vsid,valid;
2735 unsigned long llp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736
Michael Ellerman736256e2014-05-26 21:02:14 +10002737 printf("SLB contents of cpu 0x%x\n", smp_processor_id());
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738
Michael Neuling584f8b72007-12-06 17:24:48 +11002739 for (i = 0; i < mmu_slb_size; i++) {
will schmidtb3b95952007-12-07 08:22:23 +11002740 asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i));
2741 asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i));
2742 valid = (esid & SLB_ESID_V);
2743 if (valid | esid | vsid) {
2744 printf("%02d %016lx %016lx", i, esid, vsid);
2745 if (valid) {
2746 llp = vsid & SLB_VSID_LLP;
2747 if (vsid & SLB_VSID_B_1T) {
2748 printf(" 1T ESID=%9lx VSID=%13lx LLP:%3lx \n",
2749 GET_ESID_1T(esid),
2750 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT_1T,
2751 llp);
2752 } else {
2753 printf(" 256M ESID=%9lx VSID=%13lx LLP:%3lx \n",
2754 GET_ESID(esid),
2755 (vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT,
2756 llp);
2757 }
2758 } else
2759 printf("\n");
2760 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 }
2762}
Paul Mackerrasf78541dc2005-10-28 22:53:37 +10002763#endif
2764
2765#ifdef CONFIG_PPC_STD_MMU_32
2766void dump_segments(void)
2767{
2768 int i;
2769
2770 printf("sr0-15 =");
2771 for (i = 0; i < 16; ++i)
2772 printf(" %x", mfsrin(i));
2773 printf("\n");
2774}
2775#endif
2776
Benjamin Herrenschmidt5a8a1a22007-11-16 18:23:33 +11002777#ifdef CONFIG_44x
2778static void dump_tlb_44x(void)
2779{
2780 int i;
2781
2782 for (i = 0; i < PPC44x_TLB_SIZE; i++) {
2783 unsigned long w0,w1,w2;
2784 asm volatile("tlbre %0,%1,0" : "=r" (w0) : "r" (i));
2785 asm volatile("tlbre %0,%1,1" : "=r" (w1) : "r" (i));
2786 asm volatile("tlbre %0,%1,2" : "=r" (w2) : "r" (i));
2787 printf("[%02x] %08x %08x %08x ", i, w0, w1, w2);
2788 if (w0 & PPC44x_TLB_VALID) {
2789 printf("V %08x -> %01x%08x %c%c%c%c%c",
2790 w0 & PPC44x_TLB_EPN_MASK,
2791 w1 & PPC44x_TLB_ERPN_MASK,
2792 w1 & PPC44x_TLB_RPN_MASK,
2793 (w2 & PPC44x_TLB_W) ? 'W' : 'w',
2794 (w2 & PPC44x_TLB_I) ? 'I' : 'i',
2795 (w2 & PPC44x_TLB_M) ? 'M' : 'm',
2796 (w2 & PPC44x_TLB_G) ? 'G' : 'g',
2797 (w2 & PPC44x_TLB_E) ? 'E' : 'e');
2798 }
2799 printf("\n");
2800 }
2801}
2802#endif /* CONFIG_44x */
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002803
Benjamin Herrenschmidt03247152010-07-09 15:34:50 +10002804#ifdef CONFIG_PPC_BOOK3E
2805static void dump_tlb_book3e(void)
2806{
2807 u32 mmucfg, pidmask, lpidmask;
2808 u64 ramask;
2809 int i, tlb, ntlbs, pidsz, lpidsz, rasz, lrat = 0;
2810 int mmu_version;
2811 static const char *pgsz_names[] = {
2812 " 1K",
2813 " 2K",
2814 " 4K",
2815 " 8K",
2816 " 16K",
2817 " 32K",
2818 " 64K",
2819 "128K",
2820 "256K",
2821 "512K",
2822 " 1M",
2823 " 2M",
2824 " 4M",
2825 " 8M",
2826 " 16M",
2827 " 32M",
2828 " 64M",
2829 "128M",
2830 "256M",
2831 "512M",
2832 " 1G",
2833 " 2G",
2834 " 4G",
2835 " 8G",
2836 " 16G",
2837 " 32G",
2838 " 64G",
2839 "128G",
2840 "256G",
2841 "512G",
2842 " 1T",
2843 " 2T",
2844 };
2845
2846 /* Gather some infos about the MMU */
2847 mmucfg = mfspr(SPRN_MMUCFG);
2848 mmu_version = (mmucfg & 3) + 1;
2849 ntlbs = ((mmucfg >> 2) & 3) + 1;
2850 pidsz = ((mmucfg >> 6) & 0x1f) + 1;
2851 lpidsz = (mmucfg >> 24) & 0xf;
2852 rasz = (mmucfg >> 16) & 0x7f;
2853 if ((mmu_version > 1) && (mmucfg & 0x10000))
2854 lrat = 1;
2855 printf("Book3E MMU MAV=%d.0,%d TLBs,%d-bit PID,%d-bit LPID,%d-bit RA\n",
2856 mmu_version, ntlbs, pidsz, lpidsz, rasz);
2857 pidmask = (1ul << pidsz) - 1;
2858 lpidmask = (1ul << lpidsz) - 1;
2859 ramask = (1ull << rasz) - 1;
2860
2861 for (tlb = 0; tlb < ntlbs; tlb++) {
2862 u32 tlbcfg;
2863 int nent, assoc, new_cc = 1;
2864 printf("TLB %d:\n------\n", tlb);
2865 switch(tlb) {
2866 case 0:
2867 tlbcfg = mfspr(SPRN_TLB0CFG);
2868 break;
2869 case 1:
2870 tlbcfg = mfspr(SPRN_TLB1CFG);
2871 break;
2872 case 2:
2873 tlbcfg = mfspr(SPRN_TLB2CFG);
2874 break;
2875 case 3:
2876 tlbcfg = mfspr(SPRN_TLB3CFG);
2877 break;
2878 default:
2879 printf("Unsupported TLB number !\n");
2880 continue;
2881 }
2882 nent = tlbcfg & 0xfff;
2883 assoc = (tlbcfg >> 24) & 0xff;
2884 for (i = 0; i < nent; i++) {
2885 u32 mas0 = MAS0_TLBSEL(tlb);
2886 u32 mas1 = MAS1_TSIZE(BOOK3E_PAGESZ_4K);
2887 u64 mas2 = 0;
2888 u64 mas7_mas3;
2889 int esel = i, cc = i;
2890
2891 if (assoc != 0) {
2892 cc = i / assoc;
2893 esel = i % assoc;
2894 mas2 = cc * 0x1000;
2895 }
2896
2897 mas0 |= MAS0_ESEL(esel);
2898 mtspr(SPRN_MAS0, mas0);
2899 mtspr(SPRN_MAS1, mas1);
2900 mtspr(SPRN_MAS2, mas2);
2901 asm volatile("tlbre 0,0,0" : : : "memory");
2902 mas1 = mfspr(SPRN_MAS1);
2903 mas2 = mfspr(SPRN_MAS2);
2904 mas7_mas3 = mfspr(SPRN_MAS7_MAS3);
2905 if (assoc && (i % assoc) == 0)
2906 new_cc = 1;
2907 if (!(mas1 & MAS1_VALID))
2908 continue;
2909 if (assoc == 0)
2910 printf("%04x- ", i);
2911 else if (new_cc)
2912 printf("%04x-%c", cc, 'A' + esel);
2913 else
2914 printf(" |%c", 'A' + esel);
2915 new_cc = 0;
2916 printf(" %016llx %04x %s %c%c AS%c",
2917 mas2 & ~0x3ffull,
2918 (mas1 >> 16) & 0x3fff,
2919 pgsz_names[(mas1 >> 7) & 0x1f],
2920 mas1 & MAS1_IND ? 'I' : ' ',
2921 mas1 & MAS1_IPROT ? 'P' : ' ',
2922 mas1 & MAS1_TS ? '1' : '0');
2923 printf(" %c%c%c%c%c%c%c",
2924 mas2 & MAS2_X0 ? 'a' : ' ',
2925 mas2 & MAS2_X1 ? 'v' : ' ',
2926 mas2 & MAS2_W ? 'w' : ' ',
2927 mas2 & MAS2_I ? 'i' : ' ',
2928 mas2 & MAS2_M ? 'm' : ' ',
2929 mas2 & MAS2_G ? 'g' : ' ',
2930 mas2 & MAS2_E ? 'e' : ' ');
2931 printf(" %016llx", mas7_mas3 & ramask & ~0x7ffull);
2932 if (mas1 & MAS1_IND)
2933 printf(" %s\n",
2934 pgsz_names[(mas7_mas3 >> 1) & 0x1f]);
2935 else
2936 printf(" U%c%c%c S%c%c%c\n",
2937 mas7_mas3 & MAS3_UX ? 'x' : ' ',
2938 mas7_mas3 & MAS3_UW ? 'w' : ' ',
2939 mas7_mas3 & MAS3_UR ? 'r' : ' ',
2940 mas7_mas3 & MAS3_SX ? 'x' : ' ',
2941 mas7_mas3 & MAS3_SW ? 'w' : ' ',
2942 mas7_mas3 & MAS3_SR ? 'r' : ' ');
2943 }
2944 }
2945}
2946#endif /* CONFIG_PPC_BOOK3E */
2947
Michael Ellerman9f1067c22008-05-08 14:27:16 +10002948static void xmon_init(int enable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949{
Olaf Heringb13cfd172005-08-04 19:26:42 +02002950 if (enable) {
2951 __debugger = xmon;
2952 __debugger_ipi = xmon_ipi;
2953 __debugger_bpt = xmon_bpt;
2954 __debugger_sstep = xmon_sstep;
2955 __debugger_iabr_match = xmon_iabr_match;
Michael Neuling9422de32012-12-20 14:06:44 +00002956 __debugger_break_match = xmon_break_match;
Olaf Heringb13cfd172005-08-04 19:26:42 +02002957 __debugger_fault_handler = xmon_fault_handler;
2958 } else {
2959 __debugger = NULL;
2960 __debugger_ipi = NULL;
2961 __debugger_bpt = NULL;
2962 __debugger_sstep = NULL;
2963 __debugger_iabr_match = NULL;
Michael Neuling9422de32012-12-20 14:06:44 +00002964 __debugger_break_match = NULL;
Olaf Heringb13cfd172005-08-04 19:26:42 +02002965 __debugger_fault_handler = NULL;
2966 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967}
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002968
2969#ifdef CONFIG_MAGIC_SYSRQ
Dmitry Torokhov1495cc92010-08-17 21:15:46 -07002970static void sysrq_handle_xmon(int key)
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002971{
2972 /* ensure xmon is enabled */
2973 xmon_init(1);
David Howells7d12e782006-10-05 14:55:46 +01002974 debugger(get_irq_regs());
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002975}
2976
Dmitry Torokhov1495cc92010-08-17 21:15:46 -07002977static struct sysrq_key_op sysrq_xmon_op = {
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002978 .handler = sysrq_handle_xmon,
zhangwei(Jovi)90a102e2013-04-30 15:28:54 -07002979 .help_msg = "xmon(x)",
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +11002980 .action_msg = "Entering xmon",
2981};
2982
2983static int __init setup_xmon_sysrq(void)
2984{
2985 register_sysrq_key('x', &sysrq_xmon_op);
2986 return 0;
2987}
2988__initcall(setup_xmon_sysrq);
2989#endif /* CONFIG_MAGIC_SYSRQ */
Michael Ellerman476792832006-10-03 14:12:08 +10002990
Olaf Heringf5e6a282007-06-24 16:57:08 +10002991static int __initdata xmon_early, xmon_off;
Michael Ellerman476792832006-10-03 14:12:08 +10002992
2993static int __init early_parse_xmon(char *p)
2994{
2995 if (!p || strncmp(p, "early", 5) == 0) {
2996 /* just "xmon" is equivalent to "xmon=early" */
2997 xmon_init(1);
2998 xmon_early = 1;
2999 } else if (strncmp(p, "on", 2) == 0)
3000 xmon_init(1);
3001 else if (strncmp(p, "off", 3) == 0)
3002 xmon_off = 1;
3003 else if (strncmp(p, "nobt", 4) == 0)
3004 xmon_no_auto_backtrace = 1;
3005 else
3006 return 1;
3007
3008 return 0;
3009}
3010early_param("xmon", early_parse_xmon);
3011
3012void __init xmon_setup(void)
3013{
3014#ifdef CONFIG_XMON_DEFAULT
3015 if (!xmon_off)
3016 xmon_init(1);
3017#endif
3018 if (xmon_early)
3019 debugger(NULL);
3020}
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003021
Arnd Bergmanne0555952006-11-27 19:18:55 +01003022#ifdef CONFIG_SPU_BASE
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003023
3024struct spu_info {
3025 struct spu *spu;
3026 u64 saved_mfc_sr1_RW;
3027 u32 saved_spu_runcntl_RW;
Michael Ellerman24a24c82006-11-23 00:46:41 +01003028 unsigned long dump_addr;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003029 u8 stopped_ok;
3030};
3031
3032#define XMON_NUM_SPUS 16 /* Enough for current hardware */
3033
3034static struct spu_info spu_info[XMON_NUM_SPUS];
3035
3036void xmon_register_spus(struct list_head *list)
3037{
3038 struct spu *spu;
3039
3040 list_for_each_entry(spu, list, full_list) {
3041 if (spu->number >= XMON_NUM_SPUS) {
3042 WARN_ON(1);
3043 continue;
3044 }
3045
3046 spu_info[spu->number].spu = spu;
3047 spu_info[spu->number].stopped_ok = 0;
Michael Ellerman24a24c82006-11-23 00:46:41 +01003048 spu_info[spu->number].dump_addr = (unsigned long)
3049 spu_info[spu->number].spu->local_store;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003050 }
3051}
3052
3053static void stop_spus(void)
3054{
3055 struct spu *spu;
3056 int i;
3057 u64 tmp;
3058
3059 for (i = 0; i < XMON_NUM_SPUS; i++) {
3060 if (!spu_info[i].spu)
3061 continue;
3062
3063 if (setjmp(bus_error_jmp) == 0) {
3064 catch_memory_errors = 1;
3065 sync();
3066
3067 spu = spu_info[i].spu;
3068
3069 spu_info[i].saved_spu_runcntl_RW =
3070 in_be32(&spu->problem->spu_runcntl_RW);
3071
3072 tmp = spu_mfc_sr1_get(spu);
3073 spu_info[i].saved_mfc_sr1_RW = tmp;
3074
3075 tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
3076 spu_mfc_sr1_set(spu, tmp);
3077
3078 sync();
3079 __delay(200);
3080
3081 spu_info[i].stopped_ok = 1;
Michael Ellerman2a144422006-11-23 00:46:40 +01003082
3083 printf("Stopped spu %.2d (was %s)\n", i,
3084 spu_info[i].saved_spu_runcntl_RW ?
3085 "running" : "stopped");
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003086 } else {
3087 catch_memory_errors = 0;
3088 printf("*** Error stopping spu %.2d\n", i);
3089 }
3090 catch_memory_errors = 0;
3091 }
3092}
3093
3094static void restart_spus(void)
3095{
3096 struct spu *spu;
3097 int i;
3098
3099 for (i = 0; i < XMON_NUM_SPUS; i++) {
3100 if (!spu_info[i].spu)
3101 continue;
3102
3103 if (!spu_info[i].stopped_ok) {
3104 printf("*** Error, spu %d was not successfully stopped"
3105 ", not restarting\n", i);
3106 continue;
3107 }
3108
3109 if (setjmp(bus_error_jmp) == 0) {
3110 catch_memory_errors = 1;
3111 sync();
3112
3113 spu = spu_info[i].spu;
3114 spu_mfc_sr1_set(spu, spu_info[i].saved_mfc_sr1_RW);
3115 out_be32(&spu->problem->spu_runcntl_RW,
3116 spu_info[i].saved_spu_runcntl_RW);
3117
3118 sync();
3119 __delay(200);
3120
3121 printf("Restarted spu %.2d\n", i);
3122 } else {
3123 catch_memory_errors = 0;
3124 printf("*** Error restarting spu %.2d\n", i);
3125 }
3126 catch_memory_errors = 0;
3127 }
3128}
3129
Michael Ellermana8984972006-10-24 18:31:28 +02003130#define DUMP_WIDTH 23
Michael Ellerman437a0702006-11-23 00:46:39 +01003131#define DUMP_VALUE(format, field, value) \
Michael Ellermana8984972006-10-24 18:31:28 +02003132do { \
3133 if (setjmp(bus_error_jmp) == 0) { \
3134 catch_memory_errors = 1; \
3135 sync(); \
3136 printf(" %-*s = "format"\n", DUMP_WIDTH, \
Michael Ellerman437a0702006-11-23 00:46:39 +01003137 #field, value); \
Michael Ellermana8984972006-10-24 18:31:28 +02003138 sync(); \
3139 __delay(200); \
3140 } else { \
3141 catch_memory_errors = 0; \
3142 printf(" %-*s = *** Error reading field.\n", \
3143 DUMP_WIDTH, #field); \
3144 } \
3145 catch_memory_errors = 0; \
3146} while (0)
3147
Michael Ellerman437a0702006-11-23 00:46:39 +01003148#define DUMP_FIELD(obj, format, field) \
3149 DUMP_VALUE(format, field, obj->field)
3150
Michael Ellermana8984972006-10-24 18:31:28 +02003151static void dump_spu_fields(struct spu *spu)
3152{
3153 printf("Dumping spu fields at address %p:\n", spu);
3154
3155 DUMP_FIELD(spu, "0x%x", number);
3156 DUMP_FIELD(spu, "%s", name);
Michael Ellermana8984972006-10-24 18:31:28 +02003157 DUMP_FIELD(spu, "0x%lx", local_store_phys);
3158 DUMP_FIELD(spu, "0x%p", local_store);
3159 DUMP_FIELD(spu, "0x%lx", ls_size);
3160 DUMP_FIELD(spu, "0x%x", node);
3161 DUMP_FIELD(spu, "0x%lx", flags);
Michael Ellermana8984972006-10-24 18:31:28 +02003162 DUMP_FIELD(spu, "%d", class_0_pending);
Luke Browningf3d69e02008-04-27 18:41:55 +00003163 DUMP_FIELD(spu, "0x%lx", class_0_dar);
Luke Browningf3d69e02008-04-27 18:41:55 +00003164 DUMP_FIELD(spu, "0x%lx", class_1_dar);
3165 DUMP_FIELD(spu, "0x%lx", class_1_dsisr);
Michael Ellermana8984972006-10-24 18:31:28 +02003166 DUMP_FIELD(spu, "0x%lx", irqs[0]);
3167 DUMP_FIELD(spu, "0x%lx", irqs[1]);
3168 DUMP_FIELD(spu, "0x%lx", irqs[2]);
3169 DUMP_FIELD(spu, "0x%x", slb_replace);
3170 DUMP_FIELD(spu, "%d", pid);
Michael Ellermana8984972006-10-24 18:31:28 +02003171 DUMP_FIELD(spu, "0x%p", mm);
3172 DUMP_FIELD(spu, "0x%p", ctx);
3173 DUMP_FIELD(spu, "0x%p", rq);
3174 DUMP_FIELD(spu, "0x%p", timestamp);
3175 DUMP_FIELD(spu, "0x%lx", problem_phys);
3176 DUMP_FIELD(spu, "0x%p", problem);
Michael Ellerman437a0702006-11-23 00:46:39 +01003177 DUMP_VALUE("0x%x", problem->spu_runcntl_RW,
3178 in_be32(&spu->problem->spu_runcntl_RW));
3179 DUMP_VALUE("0x%x", problem->spu_status_R,
3180 in_be32(&spu->problem->spu_status_R));
3181 DUMP_VALUE("0x%x", problem->spu_npc_RW,
3182 in_be32(&spu->problem->spu_npc_RW));
Michael Ellermana8984972006-10-24 18:31:28 +02003183 DUMP_FIELD(spu, "0x%p", priv2);
Michael Ellermana9852392006-11-23 00:46:50 +01003184 DUMP_FIELD(spu, "0x%p", pdata);
Michael Ellermana8984972006-10-24 18:31:28 +02003185}
3186
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003187int
3188spu_inst_dump(unsigned long adr, long count, int praddr)
3189{
3190 return generic_inst_dump(adr, count, praddr, print_insn_spu);
3191}
3192
3193static void dump_spu_ls(unsigned long num, int subcmd)
Michael Ellerman24a24c82006-11-23 00:46:41 +01003194{
3195 unsigned long offset, addr, ls_addr;
3196
3197 if (setjmp(bus_error_jmp) == 0) {
3198 catch_memory_errors = 1;
3199 sync();
3200 ls_addr = (unsigned long)spu_info[num].spu->local_store;
3201 sync();
3202 __delay(200);
3203 } else {
3204 catch_memory_errors = 0;
3205 printf("*** Error: accessing spu info for spu %d\n", num);
3206 return;
3207 }
3208 catch_memory_errors = 0;
3209
3210 if (scanhex(&offset))
3211 addr = ls_addr + offset;
3212 else
3213 addr = spu_info[num].dump_addr;
3214
3215 if (addr >= ls_addr + LS_SIZE) {
3216 printf("*** Error: address outside of local store\n");
3217 return;
3218 }
3219
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003220 switch (subcmd) {
3221 case 'i':
3222 addr += spu_inst_dump(addr, 16, 1);
3223 last_cmd = "sdi\n";
3224 break;
3225 default:
3226 prdump(addr, 64);
3227 addr += 64;
3228 last_cmd = "sd\n";
3229 break;
3230 }
Michael Ellerman24a24c82006-11-23 00:46:41 +01003231
3232 spu_info[num].dump_addr = addr;
3233}
3234
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003235static int do_spu_cmd(void)
3236{
Michael Ellerman24a24c82006-11-23 00:46:41 +01003237 static unsigned long num = 0;
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003238 int cmd, subcmd = 0;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003239
3240 cmd = inchar();
3241 switch (cmd) {
3242 case 's':
3243 stop_spus();
3244 break;
3245 case 'r':
3246 restart_spus();
3247 break;
Michael Ellerman24a24c82006-11-23 00:46:41 +01003248 case 'd':
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003249 subcmd = inchar();
3250 if (isxdigit(subcmd) || subcmd == '\n')
3251 termch = subcmd;
3252 case 'f':
Michael Ellerman24a24c82006-11-23 00:46:41 +01003253 scanhex(&num);
3254 if (num >= XMON_NUM_SPUS || !spu_info[num].spu) {
Michael Ellermana8984972006-10-24 18:31:28 +02003255 printf("*** Error: invalid spu number\n");
Michael Ellerman24a24c82006-11-23 00:46:41 +01003256 return 0;
3257 }
3258
3259 switch (cmd) {
3260 case 'f':
3261 dump_spu_fields(spu_info[num].spu);
3262 break;
3263 default:
Michael Ellermanaf89fb82006-11-23 00:46:44 +01003264 dump_spu_ls(num, subcmd);
Michael Ellerman24a24c82006-11-23 00:46:41 +01003265 break;
3266 }
3267
Michael Ellermana8984972006-10-24 18:31:28 +02003268 break;
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003269 default:
3270 return -1;
3271 }
3272
3273 return 0;
3274}
Arnd Bergmanne0555952006-11-27 19:18:55 +01003275#else /* ! CONFIG_SPU_BASE */
Michael Ellermanff8a8f22006-10-24 18:31:27 +02003276static int do_spu_cmd(void)
3277{
3278 return -1;
3279}
3280#endif