2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Routines providing a simple monitor for use on the PowerMac.
|
|
|
|
*
|
2005-11-08 19:55:08 +08:00
|
|
|
* Copyright (C) 1996-2005 Paul Mackerras.
|
2006-10-03 12:12:08 +08:00
|
|
|
* Copyright (C) 2001 PPC64 Team, IBM Corp
|
|
|
|
* Copyrignt (C) 2006 Michael Ellerman, IBM Corp
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*/
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/reboot.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/kallsyms.h>
|
2012-08-24 06:09:12 +08:00
|
|
|
#include <linux/kmsg_dump.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/cpumask.h>
|
2011-07-23 06:24:23 +08:00
|
|
|
#include <linux/export.h>
|
2005-11-08 19:55:08 +08:00
|
|
|
#include <linux/sysrq.h>
|
2005-11-14 08:06:50 +08:00
|
|
|
#include <linux/interrupt.h>
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
#include <linux/irq.h>
|
2006-12-08 19:30:41 +08:00
|
|
|
#include <linux/bug.h>
|
2014-08-05 12:55:00 +08:00
|
|
|
#include <linux/nmi.h>
|
2014-07-15 19:43:47 +08:00
|
|
|
#include <linux/ctype.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <asm/ptrace.h>
|
|
|
|
#include <asm/string.h>
|
|
|
|
#include <asm/prom.h>
|
|
|
|
#include <asm/machdep.h>
|
2005-10-28 20:53:37 +08:00
|
|
|
#include <asm/xmon.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
#include <asm/mmu.h>
|
|
|
|
#include <asm/mmu_context.h>
|
|
|
|
#include <asm/cputable.h>
|
|
|
|
#include <asm/rtas.h>
|
|
|
|
#include <asm/sstep.h>
|
2006-10-10 09:47:07 +08:00
|
|
|
#include <asm/irq_regs.h>
|
2006-10-25 00:31:27 +08:00
|
|
|
#include <asm/spu.h>
|
|
|
|
#include <asm/spu_priv1.h>
|
2008-01-18 12:50:30 +08:00
|
|
|
#include <asm/setjmp.h>
|
2008-12-17 18:08:55 +08:00
|
|
|
#include <asm/reg.h>
|
2012-03-29 01:30:02 +08:00
|
|
|
#include <asm/debug.h>
|
2012-12-20 22:06:44 +08:00
|
|
|
#include <asm/hw_breakpoint.h>
|
2005-10-28 20:53:37 +08:00
|
|
|
|
2016-02-09 15:17:49 +08:00
|
|
|
#include <asm/opal.h>
|
|
|
|
#include <asm/firmware.h>
|
|
|
|
|
2005-10-28 20:53:37 +08:00
|
|
|
#ifdef CONFIG_PPC64
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/hvcall.h>
|
2005-10-28 20:53:37 +08:00
|
|
|
#include <asm/paca.h>
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-28 12:36:42 +08:00
|
|
|
#if defined(CONFIG_PPC_SPLPAR)
|
|
|
|
#include <asm/plpar_wrappers.h>
|
|
|
|
#else
|
|
|
|
static inline long plapr_set_ciabr(unsigned long ciabr) {return 0; };
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "nonstdio.h"
|
2006-11-23 07:46:45 +08:00
|
|
|
#include "dis-asm.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
2008-05-08 12:27:17 +08:00
|
|
|
static cpumask_t cpus_in_xmon = CPU_MASK_NONE;
|
2005-04-17 06:20:36 +08:00
|
|
|
static unsigned long xmon_taken = 1;
|
|
|
|
static int xmon_owner;
|
|
|
|
static int xmon_gate;
|
2012-09-14 07:01:31 +08:00
|
|
|
#else
|
|
|
|
#define xmon_owner 0
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
|
2010-01-12 08:50:14 +08:00
|
|
|
static unsigned long in_xmon __read_mostly = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static unsigned long adrs;
|
|
|
|
static int size = 1;
|
|
|
|
#define MAX_DUMP (128 * 1024)
|
|
|
|
static unsigned long ndump = 64;
|
|
|
|
static unsigned long nidump = 16;
|
|
|
|
static unsigned long ncsum = 4096;
|
|
|
|
static int termch;
|
|
|
|
static char tmpstr[128];
|
|
|
|
|
|
|
|
static long bus_error_jmp[JMP_BUF_LEN];
|
|
|
|
static int catch_memory_errors;
|
|
|
|
static long *xmon_fault_jmp[NR_CPUS];
|
|
|
|
|
|
|
|
/* Breakpoint stuff */
|
|
|
|
struct bpt {
|
|
|
|
unsigned long address;
|
|
|
|
unsigned int instr[2];
|
|
|
|
atomic_t ref_count;
|
|
|
|
int enabled;
|
|
|
|
unsigned long pad;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Bits in bpt.enabled */
|
2014-12-01 13:54:13 +08:00
|
|
|
#define BP_CIABR 1
|
|
|
|
#define BP_TRAP 2
|
|
|
|
#define BP_DABR 4
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define NBPTS 256
|
|
|
|
static struct bpt bpts[NBPTS];
|
|
|
|
static struct bpt dabr;
|
|
|
|
static struct bpt *iabr;
|
|
|
|
static unsigned bpinstr = 0x7fe00008; /* trap */
|
|
|
|
|
|
|
|
#define BP_NUM(bp) ((bp) - bpts + 1)
|
|
|
|
|
|
|
|
/* Prototypes */
|
|
|
|
static int cmds(struct pt_regs *);
|
|
|
|
static int mread(unsigned long, void *, int);
|
|
|
|
static int mwrite(unsigned long, void *, int);
|
|
|
|
static int handle_fault(struct pt_regs *);
|
|
|
|
static void byterev(unsigned char *, int);
|
|
|
|
static void memex(void);
|
|
|
|
static int bsesc(void);
|
|
|
|
static void dump(void);
|
|
|
|
static void prdump(unsigned long, long);
|
|
|
|
static int ppc_inst_dump(unsigned long, long, int);
|
2009-05-15 07:13:07 +08:00
|
|
|
static void dump_log_buf(void);
|
2016-02-09 15:17:49 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_PPC_POWERNV
|
|
|
|
static void dump_opal_msglog(void);
|
|
|
|
#else
|
|
|
|
static inline void dump_opal_msglog(void)
|
|
|
|
{
|
|
|
|
printf("Machine is not running OPAL firmware.\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void backtrace(struct pt_regs *);
|
|
|
|
static void excprint(struct pt_regs *);
|
|
|
|
static void prregs(struct pt_regs *);
|
|
|
|
static void memops(int);
|
|
|
|
static void memlocate(void);
|
|
|
|
static void memzcan(void);
|
|
|
|
static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned);
|
|
|
|
int skipbl(void);
|
|
|
|
int scanhex(unsigned long *valp);
|
|
|
|
static void scannl(void);
|
|
|
|
static int hexdigit(int);
|
|
|
|
void getstring(char *, int);
|
|
|
|
static void flush_input(void);
|
|
|
|
static int inchar(void);
|
|
|
|
static void take_input(char *);
|
|
|
|
static unsigned long read_spr(int);
|
|
|
|
static void write_spr(int, unsigned long);
|
|
|
|
static void super_regs(void);
|
|
|
|
static void remove_bpts(void);
|
|
|
|
static void insert_bpts(void);
|
|
|
|
static void remove_cpu_bpts(void);
|
|
|
|
static void insert_cpu_bpts(void);
|
|
|
|
static struct bpt *at_breakpoint(unsigned long pc);
|
|
|
|
static struct bpt *in_breakpoint_table(unsigned long pc, unsigned long *offp);
|
|
|
|
static int do_step(struct pt_regs *);
|
|
|
|
static void bpt_cmds(void);
|
|
|
|
static void cacheflush(void);
|
|
|
|
static int cpu_cmd(void);
|
|
|
|
static void csum(void);
|
|
|
|
static void bootcmds(void);
|
2005-10-28 20:53:37 +08:00
|
|
|
static void proccall(void);
|
2005-04-17 06:20:36 +08:00
|
|
|
void dump_segments(void);
|
|
|
|
static void symbol_lookup(void);
|
2006-09-08 22:29:21 +08:00
|
|
|
static void xmon_show_stack(unsigned long sp, unsigned long lr,
|
|
|
|
unsigned long pc);
|
2005-04-17 06:20:36 +08:00
|
|
|
static void xmon_print_symbol(unsigned long address, const char *mid,
|
|
|
|
const char *after);
|
|
|
|
static const char *getvecname(unsigned long vec);
|
|
|
|
|
2006-10-25 00:31:27 +08:00
|
|
|
static int do_spu_cmd(void);
|
|
|
|
|
2007-11-16 15:23:33 +08:00
|
|
|
#ifdef CONFIG_44x
|
|
|
|
static void dump_tlb_44x(void);
|
|
|
|
#endif
|
2010-07-09 13:34:50 +08:00
|
|
|
#ifdef CONFIG_PPC_BOOK3E
|
|
|
|
static void dump_tlb_book3e(void);
|
|
|
|
#endif
|
2007-11-16 15:23:33 +08:00
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static int xmon_no_auto_backtrace;
|
2006-09-08 22:29:21 +08:00
|
|
|
|
2005-10-28 20:53:37 +08:00
|
|
|
extern void xmon_enter(void);
|
|
|
|
extern void xmon_leave(void);
|
|
|
|
|
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
#define REG "%.16lx"
|
|
|
|
#else
|
|
|
|
#define REG "%.8lx"
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-12-02 17:10:12 +08:00
|
|
|
#ifdef __LITTLE_ENDIAN__
|
|
|
|
#define GETWORD(v) (((v)[3] << 24) + ((v)[2] << 16) + ((v)[1] << 8) + (v)[0])
|
|
|
|
#else
|
2005-04-17 06:20:36 +08:00
|
|
|
#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])
|
2013-12-02 17:10:12 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static char *help_string = "\
|
|
|
|
Commands:\n\
|
|
|
|
b show breakpoints\n\
|
|
|
|
bd set data breakpoint\n\
|
|
|
|
bi set instruction breakpoint\n\
|
|
|
|
bc clear breakpoint\n"
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
"\
|
|
|
|
c print cpus stopped in xmon\n\
|
|
|
|
c# try to switch to cpu number h (in hex)\n"
|
|
|
|
#endif
|
|
|
|
"\
|
|
|
|
C checksum\n\
|
|
|
|
d dump bytes\n\
|
|
|
|
di dump instructions\n\
|
|
|
|
df dump float values\n\
|
|
|
|
dd dump double values\n\
|
2012-09-14 07:01:31 +08:00
|
|
|
dl dump the kernel log buffer\n"
|
2016-02-09 15:17:49 +08:00
|
|
|
#ifdef CONFIG_PPC_POWERNV
|
|
|
|
"\
|
|
|
|
do dump the OPAL message log\n"
|
|
|
|
#endif
|
2012-09-14 07:01:31 +08:00
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
"\
|
|
|
|
dp[#] dump paca for current cpu, or cpu #\n\
|
|
|
|
dpa dump paca for all possible cpus\n"
|
|
|
|
#endif
|
|
|
|
"\
|
2006-03-09 03:40:28 +08:00
|
|
|
dr dump stream of raw bytes\n\
|
2005-04-17 06:20:36 +08:00
|
|
|
e print exception information\n\
|
|
|
|
f flush cache\n\
|
|
|
|
la lookup symbol+offset of specified address\n\
|
|
|
|
ls lookup address of specified symbol\n\
|
|
|
|
m examine/change memory\n\
|
|
|
|
mm move a block of memory\n\
|
|
|
|
ms set a block of memory\n\
|
|
|
|
md compare two blocks of memory\n\
|
|
|
|
ml locate a block of memory\n\
|
|
|
|
mz zero a block of memory\n\
|
|
|
|
mi show information about memory allocation\n\
|
2005-10-28 20:53:37 +08:00
|
|
|
p call a procedure\n\
|
2005-04-17 06:20:36 +08:00
|
|
|
r print registers\n\
|
2006-10-25 00:31:27 +08:00
|
|
|
s single step\n"
|
2006-11-28 02:18:55 +08:00
|
|
|
#ifdef CONFIG_SPU_BASE
|
2006-10-25 00:31:27 +08:00
|
|
|
" ss stop execution on all spus\n\
|
2006-10-25 00:31:28 +08:00
|
|
|
sr restore execution on stopped spus\n\
|
2006-11-23 07:46:41 +08:00
|
|
|
sf # dump spu fields for spu # (in hex)\n\
|
2007-02-26 17:14:06 +08:00
|
|
|
sd # dump spu local store for spu # (in hex)\n\
|
2006-11-23 07:46:44 +08:00
|
|
|
sdi # disassemble spu local store for spu # (in hex)\n"
|
2006-10-25 00:31:27 +08:00
|
|
|
#endif
|
|
|
|
" S print special registers\n\
|
2005-04-17 06:20:36 +08:00
|
|
|
t print backtrace\n\
|
|
|
|
x exit monitor and recover\n\
|
2016-01-27 08:29:44 +08:00
|
|
|
X exit monitor and don't recover\n"
|
2011-09-29 19:25:10 +08:00
|
|
|
#if defined(CONFIG_PPC64) && !defined(CONFIG_PPC_BOOK3E)
|
2005-10-28 20:53:37 +08:00
|
|
|
" u dump segment table or SLB\n"
|
2011-09-29 19:25:10 +08:00
|
|
|
#elif defined(CONFIG_PPC_STD_MMU_32)
|
2005-10-28 20:53:37 +08:00
|
|
|
" u dump segment registers\n"
|
2011-09-29 19:25:10 +08:00
|
|
|
#elif defined(CONFIG_44x) || defined(CONFIG_PPC_BOOK3E)
|
2007-11-16 15:23:33 +08:00
|
|
|
" u dump TLB\n"
|
|
|
|
#endif
|
2005-10-28 20:53:37 +08:00
|
|
|
" ? help\n"
|
2015-10-08 08:50:24 +08:00
|
|
|
" # n limit output to n lines per page (for dp, dpa, dl)\n"
|
2005-10-28 20:53:37 +08:00
|
|
|
" zr reboot\n\
|
2005-04-17 06:20:36 +08:00
|
|
|
zh halt\n"
|
|
|
|
;
|
|
|
|
|
|
|
|
static struct pt_regs *xmon_regs;
|
|
|
|
|
2005-10-28 20:53:37 +08:00
|
|
|
static inline void sync(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
asm volatile("sync; isync");
|
|
|
|
}
|
|
|
|
|
2005-10-28 20:53:37 +08:00
|
|
|
static inline void store_inst(void *p)
|
|
|
|
{
|
|
|
|
asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void cflush(void *p)
|
|
|
|
{
|
|
|
|
asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void cinval(void *p)
|
|
|
|
{
|
|
|
|
asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p));
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-28 12:36:42 +08:00
|
|
|
/**
|
|
|
|
* write_ciabr() - write the CIABR SPR
|
|
|
|
* @ciabr: The value to write.
|
|
|
|
*
|
|
|
|
* This function writes a value to the CIARB register either directly
|
|
|
|
* through mtspr instruction if the kernel is in HV privilege mode or
|
|
|
|
* call a hypervisor function to achieve the same in case the kernel
|
|
|
|
* is in supervisor privilege mode.
|
|
|
|
*/
|
|
|
|
static void write_ciabr(unsigned long ciabr)
|
|
|
|
{
|
|
|
|
if (!cpu_has_feature(CPU_FTR_ARCH_207S))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (cpu_has_feature(CPU_FTR_HVMODE)) {
|
|
|
|
mtspr(SPRN_CIABR, ciabr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
plapr_set_ciabr(ciabr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set_ciabr() - set the CIABR
|
|
|
|
* @addr: The value to set.
|
|
|
|
*
|
|
|
|
* This function sets the correct privilege value into the the HW
|
|
|
|
* breakpoint address before writing it up in the CIABR register.
|
|
|
|
*/
|
|
|
|
static void set_ciabr(unsigned long addr)
|
|
|
|
{
|
|
|
|
addr &= ~CIABR_PRIV;
|
|
|
|
|
|
|
|
if (cpu_has_feature(CPU_FTR_HVMODE))
|
|
|
|
addr |= CIABR_PRIV_HYPER;
|
|
|
|
else
|
|
|
|
addr |= CIABR_PRIV_SUPER;
|
|
|
|
write_ciabr(addr);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Disable surveillance (the service processor watchdog function)
|
|
|
|
* while we are in xmon.
|
|
|
|
* XXX we should re-enable it when we leave. :)
|
|
|
|
*/
|
|
|
|
#define SURVEILLANCE_TOKEN 9000
|
|
|
|
|
|
|
|
static inline void disable_surveillance(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_PPC_PSERIES
|
|
|
|
/* Since this can't be a module, args should end up below 4GB. */
|
|
|
|
static struct rtas_args args;
|
2015-11-24 19:26:09 +08:00
|
|
|
int token;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point we have got all the cpus we can into
|
|
|
|
* xmon, so there is hopefully no other cpu calling RTAS
|
|
|
|
* at the moment, even though we don't take rtas.lock.
|
|
|
|
* If we did try to take rtas.lock there would be a
|
|
|
|
* real possibility of deadlock.
|
|
|
|
*/
|
2015-11-24 19:26:09 +08:00
|
|
|
token = rtas_token("set-indicator");
|
|
|
|
if (token == RTAS_UNKNOWN_SERVICE)
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
2015-11-24 19:26:09 +08:00
|
|
|
|
|
|
|
rtas_call_unlocked(&args, token, 3, 1, NULL, SURVEILLANCE_TOKEN, 0, 0);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif /* CONFIG_PPC_PSERIES */
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
static int xmon_speaker;
|
|
|
|
|
|
|
|
static void get_output_lock(void)
|
|
|
|
{
|
|
|
|
int me = smp_processor_id() + 0x100;
|
|
|
|
int last_speaker = 0, prev;
|
|
|
|
long timeout;
|
|
|
|
|
|
|
|
if (xmon_speaker == me)
|
|
|
|
return;
|
2013-12-23 20:46:04 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
for (;;) {
|
2013-12-23 20:46:04 +08:00
|
|
|
last_speaker = cmpxchg(&xmon_speaker, 0, me);
|
|
|
|
if (last_speaker == 0)
|
|
|
|
return;
|
|
|
|
|
2013-12-23 20:46:05 +08:00
|
|
|
/*
|
|
|
|
* Wait a full second for the lock, we might be on a slow
|
|
|
|
* console, but check every 100us.
|
|
|
|
*/
|
|
|
|
timeout = 10000;
|
2005-04-17 06:20:36 +08:00
|
|
|
while (xmon_speaker == last_speaker) {
|
2013-12-23 20:46:05 +08:00
|
|
|
if (--timeout > 0) {
|
|
|
|
udelay(100);
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
2013-12-23 20:46:05 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* hostile takeover */
|
|
|
|
prev = cmpxchg(&xmon_speaker, last_speaker, me);
|
|
|
|
if (prev == last_speaker)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void release_output_lock(void)
|
|
|
|
{
|
|
|
|
xmon_speaker = 0;
|
|
|
|
}
|
2008-05-08 12:27:17 +08:00
|
|
|
|
|
|
|
int cpus_are_in_xmon(void)
|
|
|
|
{
|
2011-04-28 13:07:23 +08:00
|
|
|
return !cpumask_empty(&cpus_in_xmon);
|
2008-05-08 12:27:17 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
|
2009-09-23 11:51:04 +08:00
|
|
|
static inline int unrecoverable_excp(struct pt_regs *regs)
|
|
|
|
{
|
2011-09-29 20:05:28 +08:00
|
|
|
#if defined(CONFIG_4xx) || defined(CONFIG_PPC_BOOK3E)
|
2011-09-23 13:40:46 +08:00
|
|
|
/* We have no MSR_RI bit on 4xx or Book3e, so we simply return false */
|
2009-09-23 11:51:04 +08:00
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
return ((regs->msr & MSR_RI) == 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2006-01-11 08:00:05 +08:00
|
|
|
static int xmon_core(struct pt_regs *regs, int fromipi)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int cmd = 0;
|
|
|
|
struct bpt *bp;
|
|
|
|
long recurse_jmp[JMP_BUF_LEN];
|
|
|
|
unsigned long offset;
|
2007-03-20 22:48:34 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
int cpu;
|
|
|
|
int secondary;
|
|
|
|
unsigned long timeout;
|
|
|
|
#endif
|
|
|
|
|
2007-03-20 22:48:34 +08:00
|
|
|
local_irq_save(flags);
|
2014-08-05 12:55:00 +08:00
|
|
|
hard_irq_disable();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
bp = in_breakpoint_table(regs->nip, &offset);
|
|
|
|
if (bp != NULL) {
|
|
|
|
regs->nip = bp->address + offset;
|
|
|
|
atomic_dec(&bp->ref_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
remove_cpu_bpts();
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
cpu = smp_processor_id();
|
2011-04-28 13:07:23 +08:00
|
|
|
if (cpumask_test_cpu(cpu, &cpus_in_xmon)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
get_output_lock();
|
|
|
|
excprint(regs);
|
|
|
|
printf("cpu 0x%x: Exception %lx %s in xmon, "
|
|
|
|
"returning to main loop\n",
|
|
|
|
cpu, regs->trap, getvecname(TRAP(regs)));
|
2005-08-03 13:08:18 +08:00
|
|
|
release_output_lock();
|
2005-04-17 06:20:36 +08:00
|
|
|
longjmp(xmon_fault_jmp[cpu], 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setjmp(recurse_jmp) != 0) {
|
|
|
|
if (!in_xmon || !xmon_gate) {
|
2005-08-03 13:08:18 +08:00
|
|
|
get_output_lock();
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("xmon: WARNING: bad recursive fault "
|
|
|
|
"on cpu 0x%x\n", cpu);
|
2005-08-03 13:08:18 +08:00
|
|
|
release_output_lock();
|
2005-04-17 06:20:36 +08:00
|
|
|
goto waiting;
|
|
|
|
}
|
|
|
|
secondary = !(xmon_taken && cpu == xmon_owner);
|
|
|
|
goto cmdloop;
|
|
|
|
}
|
|
|
|
|
|
|
|
xmon_fault_jmp[cpu] = recurse_jmp;
|
|
|
|
|
|
|
|
bp = NULL;
|
2011-04-08 05:56:03 +08:00
|
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) == (MSR_IR|MSR_64BIT))
|
2005-04-17 06:20:36 +08:00
|
|
|
bp = at_breakpoint(regs->nip);
|
2009-09-23 11:51:04 +08:00
|
|
|
if (bp || unrecoverable_excp(regs))
|
2005-04-17 06:20:36 +08:00
|
|
|
fromipi = 0;
|
|
|
|
|
|
|
|
if (!fromipi) {
|
|
|
|
get_output_lock();
|
|
|
|
excprint(regs);
|
|
|
|
if (bp) {
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("cpu 0x%x stopped at breakpoint 0x%lx (",
|
2005-04-17 06:20:36 +08:00
|
|
|
cpu, BP_NUM(bp));
|
|
|
|
xmon_print_symbol(regs->nip, " ", ")\n");
|
|
|
|
}
|
2009-09-23 11:51:04 +08:00
|
|
|
if (unrecoverable_excp(regs))
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("WARNING: exception is not recoverable, "
|
|
|
|
"can't continue\n");
|
|
|
|
release_output_lock();
|
|
|
|
}
|
|
|
|
|
2013-12-23 20:46:06 +08:00
|
|
|
cpumask_set_cpu(cpu, &cpus_in_xmon);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
waiting:
|
|
|
|
secondary = 1;
|
|
|
|
while (secondary && !xmon_gate) {
|
|
|
|
if (in_xmon == 0) {
|
|
|
|
if (fromipi)
|
|
|
|
goto leave;
|
|
|
|
secondary = test_and_set_bit(0, &in_xmon);
|
|
|
|
}
|
|
|
|
barrier();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!secondary && !xmon_gate) {
|
|
|
|
/* we are the first cpu to come in */
|
|
|
|
/* interrupt other cpu(s) */
|
|
|
|
int ncpus = num_online_cpus();
|
|
|
|
|
|
|
|
xmon_owner = cpu;
|
|
|
|
mb();
|
|
|
|
if (ncpus > 1) {
|
2011-05-11 03:29:06 +08:00
|
|
|
smp_send_debugger_break();
|
2005-04-17 06:20:36 +08:00
|
|
|
/* wait for other cpus to come in */
|
|
|
|
for (timeout = 100000000; timeout != 0; --timeout) {
|
2011-04-28 13:07:23 +08:00
|
|
|
if (cpumask_weight(&cpus_in_xmon) >= ncpus)
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
barrier();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
remove_bpts();
|
|
|
|
disable_surveillance();
|
|
|
|
/* for breakpoint or single step, print the current instr. */
|
|
|
|
if (bp || TRAP(regs) == 0xd00)
|
|
|
|
ppc_inst_dump(regs->nip, 1, 0);
|
|
|
|
printf("enter ? for help\n");
|
|
|
|
mb();
|
|
|
|
xmon_gate = 1;
|
|
|
|
barrier();
|
|
|
|
}
|
|
|
|
|
|
|
|
cmdloop:
|
|
|
|
while (in_xmon) {
|
|
|
|
if (secondary) {
|
|
|
|
if (cpu == xmon_owner) {
|
|
|
|
if (!test_and_set_bit(0, &xmon_taken)) {
|
|
|
|
secondary = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* missed it */
|
|
|
|
while (cpu == xmon_owner)
|
|
|
|
barrier();
|
|
|
|
}
|
|
|
|
barrier();
|
|
|
|
} else {
|
|
|
|
cmd = cmds(regs);
|
|
|
|
if (cmd != 0) {
|
|
|
|
/* exiting xmon */
|
|
|
|
insert_bpts();
|
|
|
|
xmon_gate = 0;
|
|
|
|
wmb();
|
|
|
|
in_xmon = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* have switched to some other cpu */
|
|
|
|
secondary = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
leave:
|
2011-04-28 13:07:23 +08:00
|
|
|
cpumask_clear_cpu(cpu, &cpus_in_xmon);
|
2005-04-17 06:20:36 +08:00
|
|
|
xmon_fault_jmp[cpu] = NULL;
|
|
|
|
#else
|
|
|
|
/* UP is simple... */
|
|
|
|
if (in_xmon) {
|
|
|
|
printf("Exception %lx %s in xmon, returning to main loop\n",
|
|
|
|
regs->trap, getvecname(TRAP(regs)));
|
|
|
|
longjmp(xmon_fault_jmp[0], 1);
|
|
|
|
}
|
|
|
|
if (setjmp(recurse_jmp) == 0) {
|
|
|
|
xmon_fault_jmp[0] = recurse_jmp;
|
|
|
|
in_xmon = 1;
|
|
|
|
|
|
|
|
excprint(regs);
|
|
|
|
bp = at_breakpoint(regs->nip);
|
|
|
|
if (bp) {
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("Stopped at breakpoint %lx (", BP_NUM(bp));
|
2005-04-17 06:20:36 +08:00
|
|
|
xmon_print_symbol(regs->nip, " ", ")\n");
|
|
|
|
}
|
2009-09-23 11:51:04 +08:00
|
|
|
if (unrecoverable_excp(regs))
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("WARNING: exception is not recoverable, "
|
|
|
|
"can't continue\n");
|
|
|
|
remove_bpts();
|
|
|
|
disable_surveillance();
|
|
|
|
/* for breakpoint or single step, print the current instr. */
|
|
|
|
if (bp || TRAP(regs) == 0xd00)
|
|
|
|
ppc_inst_dump(regs->nip, 1, 0);
|
|
|
|
printf("enter ? for help\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = cmds(regs);
|
|
|
|
|
|
|
|
insert_bpts();
|
|
|
|
in_xmon = 0;
|
|
|
|
#endif
|
|
|
|
|
2009-10-05 12:46:05 +08:00
|
|
|
#ifdef CONFIG_BOOKE
|
|
|
|
if (regs->msr & MSR_DE) {
|
|
|
|
bp = at_breakpoint(regs->nip);
|
|
|
|
if (bp != NULL) {
|
|
|
|
regs->nip = (unsigned long) &bp->instr[0];
|
|
|
|
atomic_inc(&bp->ref_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2011-04-08 05:56:03 +08:00
|
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) == (MSR_IR|MSR_64BIT)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
bp = at_breakpoint(regs->nip);
|
|
|
|
if (bp != NULL) {
|
|
|
|
int stepped = emulate_step(regs, bp->instr[0]);
|
|
|
|
if (stepped == 0) {
|
|
|
|
regs->nip = (unsigned long) &bp->instr[0];
|
|
|
|
atomic_inc(&bp->ref_count);
|
|
|
|
} else if (stepped < 0) {
|
|
|
|
printf("Couldn't single-step %s instruction\n",
|
|
|
|
(IS_RFID(bp->instr[0])? "rfid": "mtmsrd"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-10-05 12:46:05 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
insert_cpu_bpts();
|
|
|
|
|
2014-08-05 12:55:00 +08:00
|
|
|
touch_nmi_watchdog();
|
2007-03-20 22:48:34 +08:00
|
|
|
local_irq_restore(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-10-03 19:32:49 +08:00
|
|
|
return cmd != 'X' && cmd != EOF;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int xmon(struct pt_regs *excp)
|
|
|
|
{
|
|
|
|
struct pt_regs regs;
|
|
|
|
|
|
|
|
if (excp == NULL) {
|
2008-12-17 18:08:55 +08:00
|
|
|
ppc_save_regs(®s);
|
2005-04-17 06:20:36 +08:00
|
|
|
excp = ®s;
|
|
|
|
}
|
2006-10-25 00:31:27 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return xmon_core(excp, 0);
|
|
|
|
}
|
2005-10-28 20:53:37 +08:00
|
|
|
EXPORT_SYMBOL(xmon);
|
|
|
|
|
2006-10-10 09:47:07 +08:00
|
|
|
irqreturn_t xmon_irq(int irq, void *d)
|
2005-10-28 20:53:37 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
local_irq_save(flags);
|
|
|
|
printf("Keyboard interrupt\n");
|
2006-10-10 09:47:07 +08:00
|
|
|
xmon(get_irq_regs());
|
2005-10-28 20:53:37 +08:00
|
|
|
local_irq_restore(flags);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-11 08:00:05 +08:00
|
|
|
static int xmon_bpt(struct pt_regs *regs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct bpt *bp;
|
|
|
|
unsigned long offset;
|
|
|
|
|
2011-04-08 05:56:03 +08:00
|
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Are we at the trap at bp->instr[1] for some bp? */
|
|
|
|
bp = in_breakpoint_table(regs->nip, &offset);
|
|
|
|
if (bp != NULL && offset == 4) {
|
|
|
|
regs->nip = bp->address + 4;
|
|
|
|
atomic_dec(&bp->ref_count);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Are we at a breakpoint? */
|
|
|
|
bp = at_breakpoint(regs->nip);
|
|
|
|
if (!bp)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
xmon_core(regs, 0);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-01-11 08:00:05 +08:00
|
|
|
static int xmon_sstep(struct pt_regs *regs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (user_mode(regs))
|
|
|
|
return 0;
|
|
|
|
xmon_core(regs, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-12-20 22:06:44 +08:00
|
|
|
static int xmon_break_match(struct pt_regs *regs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-04-08 05:56:03 +08:00
|
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2005-09-10 14:01:11 +08:00
|
|
|
if (dabr.enabled == 0)
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
xmon_core(regs, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-01-11 08:00:05 +08:00
|
|
|
static int xmon_iabr_match(struct pt_regs *regs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-04-08 05:56:03 +08:00
|
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2008-05-08 12:27:16 +08:00
|
|
|
if (iabr == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
xmon_core(regs, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-01-11 08:00:05 +08:00
|
|
|
static int xmon_ipi(struct pt_regs *regs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_SMP
|
2011-04-28 13:07:23 +08:00
|
|
|
if (in_xmon && !cpumask_test_cpu(smp_processor_id(), &cpus_in_xmon))
|
2005-04-17 06:20:36 +08:00
|
|
|
xmon_core(regs, 1);
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-01-11 08:00:05 +08:00
|
|
|
static int xmon_fault_handler(struct pt_regs *regs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct bpt *bp;
|
|
|
|
unsigned long offset;
|
|
|
|
|
|
|
|
if (in_xmon && catch_memory_errors)
|
|
|
|
handle_fault(regs); /* doesn't return */
|
|
|
|
|
2011-04-08 05:56:03 +08:00
|
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) == (MSR_IR|MSR_64BIT)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
bp = in_breakpoint_table(regs->nip, &offset);
|
|
|
|
if (bp != NULL) {
|
|
|
|
regs->nip = bp->address + offset;
|
|
|
|
atomic_dec(&bp->ref_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bpt *at_breakpoint(unsigned long pc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct bpt *bp;
|
|
|
|
|
|
|
|
bp = bpts;
|
|
|
|
for (i = 0; i < NBPTS; ++i, ++bp)
|
|
|
|
if (bp->enabled && pc == bp->address)
|
|
|
|
return bp;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bpt *in_breakpoint_table(unsigned long nip, unsigned long *offp)
|
|
|
|
{
|
|
|
|
unsigned long off;
|
|
|
|
|
|
|
|
off = nip - (unsigned long) bpts;
|
|
|
|
if (off >= sizeof(bpts))
|
|
|
|
return NULL;
|
|
|
|
off %= sizeof(struct bpt);
|
|
|
|
if (off != offsetof(struct bpt, instr[0])
|
|
|
|
&& off != offsetof(struct bpt, instr[1]))
|
|
|
|
return NULL;
|
|
|
|
*offp = off - offsetof(struct bpt, instr[0]);
|
|
|
|
return (struct bpt *) (nip - off);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bpt *new_breakpoint(unsigned long a)
|
|
|
|
{
|
|
|
|
struct bpt *bp;
|
|
|
|
|
|
|
|
a &= ~3UL;
|
|
|
|
bp = at_breakpoint(a);
|
|
|
|
if (bp)
|
|
|
|
return bp;
|
|
|
|
|
|
|
|
for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
|
|
|
|
if (!bp->enabled && atomic_read(&bp->ref_count) == 0) {
|
|
|
|
bp->address = a;
|
|
|
|
bp->instr[1] = bpinstr;
|
|
|
|
store_inst(&bp->instr[1]);
|
|
|
|
return bp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Sorry, no free breakpoints. Please clear one first.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void insert_bpts(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct bpt *bp;
|
|
|
|
|
|
|
|
bp = bpts;
|
|
|
|
for (i = 0; i < NBPTS; ++i, ++bp) {
|
2014-12-01 13:54:13 +08:00
|
|
|
if ((bp->enabled & (BP_TRAP|BP_CIABR)) == 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
if (mread(bp->address, &bp->instr[0], 4) != 4) {
|
|
|
|
printf("Couldn't read instruction at %lx, "
|
|
|
|
"disabling breakpoint there\n", bp->address);
|
|
|
|
bp->enabled = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (IS_MTMSRD(bp->instr[0]) || IS_RFID(bp->instr[0])) {
|
|
|
|
printf("Breakpoint at %lx is on an mtmsrd or rfid "
|
|
|
|
"instruction, disabling it\n", bp->address);
|
|
|
|
bp->enabled = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
store_inst(&bp->instr[0]);
|
2014-12-01 13:54:13 +08:00
|
|
|
if (bp->enabled & BP_CIABR)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
if (mwrite(bp->address, &bpinstr, 4) != 4) {
|
|
|
|
printf("Couldn't write instruction at %lx, "
|
|
|
|
"disabling breakpoint there\n", bp->address);
|
|
|
|
bp->enabled &= ~BP_TRAP;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
store_inst((void *)bp->address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void insert_cpu_bpts(void)
|
|
|
|
{
|
2012-12-20 22:06:44 +08:00
|
|
|
struct arch_hw_breakpoint brk;
|
|
|
|
|
|
|
|
if (dabr.enabled) {
|
|
|
|
brk.address = dabr.address;
|
|
|
|
brk.type = (dabr.enabled & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
|
|
|
|
brk.len = 8;
|
2014-04-30 03:25:17 +08:00
|
|
|
__set_breakpoint(&brk);
|
2012-12-20 22:06:44 +08:00
|
|
|
}
|
2014-11-28 12:36:42 +08:00
|
|
|
|
|
|
|
if (iabr)
|
|
|
|
set_ciabr(iabr->address);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void remove_bpts(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct bpt *bp;
|
|
|
|
unsigned instr;
|
|
|
|
|
|
|
|
bp = bpts;
|
|
|
|
for (i = 0; i < NBPTS; ++i, ++bp) {
|
2014-12-01 13:54:13 +08:00
|
|
|
if ((bp->enabled & (BP_TRAP|BP_CIABR)) != BP_TRAP)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
if (mread(bp->address, &instr, 4) == 4
|
|
|
|
&& instr == bpinstr
|
|
|
|
&& mwrite(bp->address, &bp->instr, 4) != 4)
|
|
|
|
printf("Couldn't remove breakpoint at %lx\n",
|
|
|
|
bp->address);
|
|
|
|
else
|
|
|
|
store_inst((void *)bp->address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void remove_cpu_bpts(void)
|
|
|
|
{
|
2012-12-20 22:06:44 +08:00
|
|
|
hw_breakpoint_disable();
|
2014-11-28 12:36:42 +08:00
|
|
|
write_ciabr(0);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-10-08 08:50:23 +08:00
|
|
|
static void set_lpp_cmd(void)
|
|
|
|
{
|
|
|
|
unsigned long lpp;
|
|
|
|
|
|
|
|
if (!scanhex(&lpp)) {
|
|
|
|
printf("Invalid number.\n");
|
|
|
|
lpp = 0;
|
|
|
|
}
|
|
|
|
xmon_set_pagination_lpp(lpp);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Command interpreting routine */
|
|
|
|
static char *last_cmd;
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmds(struct pt_regs *excp)
|
|
|
|
{
|
|
|
|
int cmd = 0;
|
|
|
|
|
|
|
|
last_cmd = NULL;
|
|
|
|
xmon_regs = excp;
|
2006-09-08 22:29:21 +08:00
|
|
|
|
|
|
|
if (!xmon_no_auto_backtrace) {
|
|
|
|
xmon_no_auto_backtrace = 1;
|
|
|
|
xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
for(;;) {
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
printf("%x:", smp_processor_id());
|
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
printf("mon> ");
|
|
|
|
flush_input();
|
|
|
|
termch = 0;
|
|
|
|
cmd = skipbl();
|
|
|
|
if( cmd == '\n' ) {
|
|
|
|
if (last_cmd == NULL)
|
|
|
|
continue;
|
|
|
|
take_input(last_cmd);
|
|
|
|
last_cmd = NULL;
|
|
|
|
cmd = inchar();
|
|
|
|
}
|
|
|
|
switch (cmd) {
|
|
|
|
case 'm':
|
|
|
|
cmd = inchar();
|
|
|
|
switch (cmd) {
|
|
|
|
case 'm':
|
|
|
|
case 's':
|
|
|
|
case 'd':
|
|
|
|
memops(cmd);
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
memlocate();
|
|
|
|
break;
|
|
|
|
case 'z':
|
|
|
|
memzcan();
|
|
|
|
break;
|
|
|
|
case 'i':
|
2011-03-25 06:18:15 +08:00
|
|
|
show_mem(0);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
termch = cmd;
|
|
|
|
memex();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
dump();
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
symbol_lookup();
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
prregs(excp); /* print regs */
|
|
|
|
break;
|
|
|
|
case 'e':
|
|
|
|
excprint(excp);
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
super_regs();
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
backtrace(excp);
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
cacheflush();
|
|
|
|
break;
|
|
|
|
case 's':
|
2006-10-25 00:31:27 +08:00
|
|
|
if (do_spu_cmd() == 0)
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (do_step(excp))
|
|
|
|
return cmd;
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
case 'X':
|
2005-11-30 13:54:12 +08:00
|
|
|
return cmd;
|
2005-04-17 06:20:36 +08:00
|
|
|
case EOF:
|
2005-11-30 13:54:12 +08:00
|
|
|
printf(" <no input ...>\n");
|
|
|
|
mdelay(2000);
|
2005-04-17 06:20:36 +08:00
|
|
|
return cmd;
|
|
|
|
case '?':
|
2007-07-18 17:26:40 +08:00
|
|
|
xmon_puts(help_string);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2015-10-08 08:50:23 +08:00
|
|
|
case '#':
|
|
|
|
set_lpp_cmd();
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
case 'b':
|
|
|
|
bpt_cmds();
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
csum();
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
if (cpu_cmd())
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 'z':
|
|
|
|
bootcmds();
|
|
|
|
break;
|
2005-10-28 20:53:37 +08:00
|
|
|
case 'p':
|
|
|
|
proccall();
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2005-10-28 20:53:37 +08:00
|
|
|
#ifdef CONFIG_PPC_STD_MMU
|
2005-04-17 06:20:36 +08:00
|
|
|
case 'u':
|
|
|
|
dump_segments();
|
|
|
|
break;
|
2014-11-12 13:54:54 +08:00
|
|
|
#elif defined(CONFIG_44x)
|
2007-11-16 15:23:33 +08:00
|
|
|
case 'u':
|
|
|
|
dump_tlb_44x();
|
|
|
|
break;
|
2011-09-29 19:25:10 +08:00
|
|
|
#elif defined(CONFIG_PPC_BOOK3E)
|
2010-07-09 13:34:50 +08:00
|
|
|
case 'u':
|
|
|
|
dump_tlb_book3e();
|
|
|
|
break;
|
2005-10-28 20:53:37 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
|
|
|
printf("Unrecognized command: ");
|
2012-08-24 06:09:13 +08:00
|
|
|
do {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (' ' < cmd && cmd <= '~')
|
|
|
|
putchar(cmd);
|
|
|
|
else
|
|
|
|
printf("\\x%x", cmd);
|
|
|
|
cmd = inchar();
|
2012-08-24 06:09:13 +08:00
|
|
|
} while (cmd != '\n');
|
2005-04-17 06:20:36 +08:00
|
|
|
printf(" (type ? for help)\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-05 12:46:05 +08:00
|
|
|
#ifdef CONFIG_BOOKE
|
|
|
|
static int do_step(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
regs->msr |= MSR_DE;
|
|
|
|
mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) | DBCR0_IC | DBCR0_IDM);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#else
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Step a single instruction.
|
|
|
|
* Some instructions we emulate, others we execute with MSR_SE set.
|
|
|
|
*/
|
|
|
|
static int do_step(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
unsigned int instr;
|
|
|
|
int stepped;
|
|
|
|
|
|
|
|
/* check we are in 64-bit kernel mode, translation enabled */
|
2011-04-08 05:56:03 +08:00
|
|
|
if ((regs->msr & (MSR_64BIT|MSR_PR|MSR_IR)) == (MSR_64BIT|MSR_IR)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (mread(regs->nip, &instr, 4) == 4) {
|
|
|
|
stepped = emulate_step(regs, instr);
|
|
|
|
if (stepped < 0) {
|
|
|
|
printf("Couldn't single-step %s instruction\n",
|
|
|
|
(IS_RFID(instr)? "rfid": "mtmsrd"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (stepped > 0) {
|
|
|
|
regs->trap = 0xd00 | (regs->trap & 1);
|
|
|
|
printf("stepped to ");
|
|
|
|
xmon_print_symbol(regs->nip, " ", "\n");
|
|
|
|
ppc_inst_dump(regs->nip, 1, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
regs->msr |= MSR_SE;
|
|
|
|
return 1;
|
|
|
|
}
|
2009-10-05 12:46:05 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static void bootcmds(void)
|
|
|
|
{
|
|
|
|
int cmd;
|
|
|
|
|
|
|
|
cmd = inchar();
|
|
|
|
if (cmd == 'r')
|
|
|
|
ppc_md.restart(NULL);
|
|
|
|
else if (cmd == 'h')
|
|
|
|
ppc_md.halt();
|
|
|
|
else if (cmd == 'p')
|
2014-10-13 22:01:09 +08:00
|
|
|
if (pm_power_off)
|
|
|
|
pm_power_off();
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int cpu_cmd(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_SMP
|
2013-09-03 18:16:23 +08:00
|
|
|
unsigned long cpu, first_cpu, last_cpu;
|
2005-04-17 06:20:36 +08:00
|
|
|
int timeout;
|
|
|
|
|
|
|
|
if (!scanhex(&cpu)) {
|
|
|
|
/* print cpus waiting or in xmon */
|
|
|
|
printf("cpus stopped:");
|
2013-09-03 18:16:23 +08:00
|
|
|
last_cpu = first_cpu = NR_CPUS;
|
2012-06-29 03:28:57 +08:00
|
|
|
for_each_possible_cpu(cpu) {
|
2011-04-28 13:07:23 +08:00
|
|
|
if (cpumask_test_cpu(cpu, &cpus_in_xmon)) {
|
2013-09-03 18:16:23 +08:00
|
|
|
if (cpu == last_cpu + 1) {
|
|
|
|
last_cpu = cpu;
|
|
|
|
} else {
|
|
|
|
if (last_cpu != first_cpu)
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("-0x%lx", last_cpu);
|
2013-09-03 18:16:23 +08:00
|
|
|
last_cpu = first_cpu = cpu;
|
2014-05-26 19:02:14 +08:00
|
|
|
printf(" 0x%lx", cpu);
|
2013-09-03 18:16:23 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2013-09-03 18:16:23 +08:00
|
|
|
if (last_cpu != first_cpu)
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("-0x%lx", last_cpu);
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* try to switch to cpu specified */
|
2011-04-28 13:07:23 +08:00
|
|
|
if (!cpumask_test_cpu(cpu, &cpus_in_xmon)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("cpu 0x%x isn't in xmon\n", cpu);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
xmon_taken = 0;
|
|
|
|
mb();
|
|
|
|
xmon_owner = cpu;
|
|
|
|
timeout = 10000000;
|
|
|
|
while (!xmon_taken) {
|
|
|
|
if (--timeout == 0) {
|
|
|
|
if (test_and_set_bit(0, &xmon_taken))
|
|
|
|
break;
|
|
|
|
/* take control back */
|
|
|
|
mb();
|
|
|
|
xmon_owner = smp_processor_id();
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("cpu 0x%x didn't take control\n", cpu);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
barrier();
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned short fcstab[256] = {
|
|
|
|
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
|
|
|
|
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
|
|
|
|
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
|
|
|
|
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
|
|
|
|
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
|
|
|
|
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
|
|
|
|
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
|
|
|
|
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
|
|
|
|
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
|
|
|
|
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
|
|
|
|
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
|
|
|
|
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
|
|
|
|
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
|
|
|
|
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
|
|
|
|
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
|
|
|
|
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
|
|
|
|
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
|
|
|
|
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
|
|
|
|
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
|
|
|
|
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
|
|
|
|
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
|
|
|
|
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
|
|
|
|
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
|
|
|
|
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
|
|
|
|
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
|
|
|
|
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
|
|
|
|
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
|
|
|
|
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
|
|
|
|
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
|
|
|
|
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
|
|
|
|
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
|
|
|
|
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
|
|
|
|
};
|
|
|
|
|
|
|
|
#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
|
|
|
|
|
|
|
|
static void
|
|
|
|
csum(void)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
unsigned short fcs;
|
|
|
|
unsigned char v;
|
|
|
|
|
|
|
|
if (!scanhex(&adrs))
|
|
|
|
return;
|
|
|
|
if (!scanhex(&ncsum))
|
|
|
|
return;
|
|
|
|
fcs = 0xffff;
|
|
|
|
for (i = 0; i < ncsum; ++i) {
|
|
|
|
if (mread(adrs+i, &v, 1) == 0) {
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("csum stopped at "REG"\n", adrs+i);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
fcs = FCS(fcs, v);
|
|
|
|
}
|
|
|
|
printf("%x\n", fcs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if this is a suitable place to put a breakpoint.
|
|
|
|
*/
|
|
|
|
static long check_bp_loc(unsigned long addr)
|
|
|
|
{
|
|
|
|
unsigned int instr;
|
|
|
|
|
|
|
|
addr &= ~3;
|
2005-12-04 15:39:15 +08:00
|
|
|
if (!is_kernel_addr(addr)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("Breakpoints may only be placed at kernel addresses\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!mread(addr, &instr, sizeof(instr))) {
|
|
|
|
printf("Can't read instruction at address %lx\n", addr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (IS_MTMSRD(instr) || IS_RFID(instr)) {
|
|
|
|
printf("Breakpoints may not be placed on mtmsrd or rfid "
|
|
|
|
"instructions\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-08-24 06:09:13 +08:00
|
|
|
static char *breakpoint_help_string =
|
2005-04-17 06:20:36 +08:00
|
|
|
"Breakpoint command usage:\n"
|
|
|
|
"b show breakpoints\n"
|
|
|
|
"b <addr> [cnt] set breakpoint at given instr addr\n"
|
|
|
|
"bc clear all breakpoints\n"
|
|
|
|
"bc <n/addr> clear breakpoint number n or at addr\n"
|
2014-11-28 12:36:42 +08:00
|
|
|
"bi <addr> [cnt] set hardware instr breakpoint (POWER8 only)\n"
|
2005-04-17 06:20:36 +08:00
|
|
|
"bd <addr> [cnt] set hardware data breakpoint\n"
|
|
|
|
"";
|
|
|
|
|
|
|
|
static void
|
|
|
|
bpt_cmds(void)
|
|
|
|
{
|
|
|
|
int cmd;
|
|
|
|
unsigned long a;
|
|
|
|
int mode, i;
|
|
|
|
struct bpt *bp;
|
|
|
|
const char badaddr[] = "Only kernel addresses are permitted "
|
|
|
|
"for breakpoints\n";
|
|
|
|
|
|
|
|
cmd = inchar();
|
|
|
|
switch (cmd) {
|
2005-10-28 20:53:37 +08:00
|
|
|
#ifndef CONFIG_8xx
|
2005-04-17 06:20:36 +08:00
|
|
|
case 'd': /* bd - hardware data breakpoint */
|
|
|
|
mode = 7;
|
|
|
|
cmd = inchar();
|
|
|
|
if (cmd == 'r')
|
|
|
|
mode = 5;
|
|
|
|
else if (cmd == 'w')
|
|
|
|
mode = 6;
|
|
|
|
else
|
|
|
|
termch = cmd;
|
|
|
|
dabr.address = 0;
|
|
|
|
dabr.enabled = 0;
|
|
|
|
if (scanhex(&dabr.address)) {
|
2005-12-04 15:39:15 +08:00
|
|
|
if (!is_kernel_addr(dabr.address)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printf(badaddr);
|
|
|
|
break;
|
|
|
|
}
|
2012-12-20 22:06:44 +08:00
|
|
|
dabr.address &= ~HW_BRK_TYPE_DABR;
|
2005-04-17 06:20:36 +08:00
|
|
|
dabr.enabled = mode | BP_DABR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'i': /* bi - hardware instr breakpoint */
|
2014-11-28 12:36:42 +08:00
|
|
|
if (!cpu_has_feature(CPU_FTR_ARCH_207S)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("Hardware instruction breakpoint "
|
|
|
|
"not supported on this cpu\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (iabr) {
|
2014-12-01 13:54:13 +08:00
|
|
|
iabr->enabled &= ~BP_CIABR;
|
2005-04-17 06:20:36 +08:00
|
|
|
iabr = NULL;
|
|
|
|
}
|
|
|
|
if (!scanhex(&a))
|
|
|
|
break;
|
|
|
|
if (!check_bp_loc(a))
|
|
|
|
break;
|
|
|
|
bp = new_breakpoint(a);
|
|
|
|
if (bp != NULL) {
|
2014-12-01 13:54:13 +08:00
|
|
|
bp->enabled |= BP_CIABR;
|
2005-04-17 06:20:36 +08:00
|
|
|
iabr = bp;
|
|
|
|
}
|
|
|
|
break;
|
2005-10-28 20:53:37 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
case 'c':
|
|
|
|
if (!scanhex(&a)) {
|
|
|
|
/* clear all breakpoints */
|
|
|
|
for (i = 0; i < NBPTS; ++i)
|
|
|
|
bpts[i].enabled = 0;
|
|
|
|
iabr = NULL;
|
|
|
|
dabr.enabled = 0;
|
|
|
|
printf("All breakpoints cleared\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a <= NBPTS && a >= 1) {
|
|
|
|
/* assume a breakpoint number */
|
|
|
|
bp = &bpts[a-1]; /* bp nums are 1 based */
|
|
|
|
} else {
|
|
|
|
/* assume a breakpoint address */
|
|
|
|
bp = at_breakpoint(a);
|
2008-05-08 12:27:16 +08:00
|
|
|
if (bp == NULL) {
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("No breakpoint at %lx\n", a);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("Cleared breakpoint %lx (", BP_NUM(bp));
|
2005-04-17 06:20:36 +08:00
|
|
|
xmon_print_symbol(bp->address, " ", ")\n");
|
|
|
|
bp->enabled = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
termch = cmd;
|
2012-08-24 06:09:13 +08:00
|
|
|
cmd = skipbl();
|
2005-04-17 06:20:36 +08:00
|
|
|
if (cmd == '?') {
|
|
|
|
printf(breakpoint_help_string);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
termch = cmd;
|
|
|
|
if (!scanhex(&a)) {
|
|
|
|
/* print all breakpoints */
|
|
|
|
printf(" type address\n");
|
|
|
|
if (dabr.enabled) {
|
2005-10-28 20:53:37 +08:00
|
|
|
printf(" data "REG" [", dabr.address);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (dabr.enabled & 1)
|
|
|
|
printf("r");
|
|
|
|
if (dabr.enabled & 2)
|
|
|
|
printf("w");
|
|
|
|
printf("]\n");
|
|
|
|
}
|
|
|
|
for (bp = bpts; bp < &bpts[NBPTS]; ++bp) {
|
|
|
|
if (!bp->enabled)
|
|
|
|
continue;
|
|
|
|
printf("%2x %s ", BP_NUM(bp),
|
2014-12-01 13:54:13 +08:00
|
|
|
(bp->enabled & BP_CIABR) ? "inst": "trap");
|
2005-04-17 06:20:36 +08:00
|
|
|
xmon_print_symbol(bp->address, " ", "\n");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!check_bp_loc(a))
|
|
|
|
break;
|
|
|
|
bp = new_breakpoint(a);
|
|
|
|
if (bp != NULL)
|
|
|
|
bp->enabled |= BP_TRAP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Very cheap human name for vector lookup. */
|
|
|
|
static
|
|
|
|
const char *getvecname(unsigned long vec)
|
|
|
|
{
|
|
|
|
char *ret;
|
|
|
|
|
|
|
|
switch (vec) {
|
|
|
|
case 0x100: ret = "(System Reset)"; break;
|
|
|
|
case 0x200: ret = "(Machine Check)"; break;
|
|
|
|
case 0x300: ret = "(Data Access)"; break;
|
|
|
|
case 0x380: ret = "(Data SLB Access)"; break;
|
|
|
|
case 0x400: ret = "(Instruction Access)"; break;
|
|
|
|
case 0x480: ret = "(Instruction SLB Access)"; break;
|
|
|
|
case 0x500: ret = "(Hardware Interrupt)"; break;
|
|
|
|
case 0x600: ret = "(Alignment)"; break;
|
|
|
|
case 0x700: ret = "(Program Check)"; break;
|
|
|
|
case 0x800: ret = "(FPU Unavailable)"; break;
|
|
|
|
case 0x900: ret = "(Decrementer)"; break;
|
2013-08-15 13:22:16 +08:00
|
|
|
case 0x980: ret = "(Hypervisor Decrementer)"; break;
|
|
|
|
case 0xa00: ret = "(Doorbell)"; break;
|
2005-04-17 06:20:36 +08:00
|
|
|
case 0xc00: ret = "(System Call)"; break;
|
|
|
|
case 0xd00: ret = "(Single Step)"; break;
|
2013-08-15 13:22:16 +08:00
|
|
|
case 0xe40: ret = "(Emulation Assist)"; break;
|
|
|
|
case 0xe60: ret = "(HMI)"; break;
|
|
|
|
case 0xe80: ret = "(Hypervisor Doorbell)"; break;
|
2005-04-17 06:20:36 +08:00
|
|
|
case 0xf00: ret = "(Performance Monitor)"; break;
|
|
|
|
case 0xf20: ret = "(Altivec Unavailable)"; break;
|
|
|
|
case 0x1300: ret = "(Instruction Breakpoint)"; break;
|
2013-08-15 13:22:16 +08:00
|
|
|
case 0x1500: ret = "(Denormalisation)"; break;
|
|
|
|
case 0x1700: ret = "(Altivec Assist)"; break;
|
2005-04-17 06:20:36 +08:00
|
|
|
default: ret = "";
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void get_function_bounds(unsigned long pc, unsigned long *startp,
|
|
|
|
unsigned long *endp)
|
|
|
|
{
|
|
|
|
unsigned long size, offset;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
*startp = *endp = 0;
|
|
|
|
if (pc == 0)
|
|
|
|
return;
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
2007-05-08 15:28:41 +08:00
|
|
|
name = kallsyms_lookup(pc, &size, &offset, NULL, tmpstr);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (name != NULL) {
|
|
|
|
*startp = pc - offset;
|
|
|
|
*endp = pc - offset + size;
|
|
|
|
}
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
}
|
|
|
|
|
2008-04-17 12:34:59 +08:00
|
|
|
#define LRSAVE_OFFSET (STACK_FRAME_LR_SAVE * sizeof(unsigned long))
|
|
|
|
#define MARKER_OFFSET (STACK_FRAME_MARKER * sizeof(unsigned long))
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void xmon_show_stack(unsigned long sp, unsigned long lr,
|
|
|
|
unsigned long pc)
|
|
|
|
{
|
2012-10-09 12:20:36 +08:00
|
|
|
int max_to_print = 64;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long ip;
|
|
|
|
unsigned long newsp;
|
|
|
|
unsigned long marker;
|
|
|
|
struct pt_regs regs;
|
|
|
|
|
2012-10-09 12:20:36 +08:00
|
|
|
while (max_to_print--) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (sp < PAGE_OFFSET) {
|
|
|
|
if (sp != 0)
|
|
|
|
printf("SP (%lx) is in userspace\n", sp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-10-28 20:53:37 +08:00
|
|
|
if (!mread(sp + LRSAVE_OFFSET, &ip, sizeof(unsigned long))
|
2005-04-17 06:20:36 +08:00
|
|
|
|| !mread(sp, &newsp, sizeof(unsigned long))) {
|
|
|
|
printf("Couldn't read stack frame at %lx\n", sp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For the first stack frame, try to work out if
|
|
|
|
* LR and/or the saved LR value in the bottommost
|
|
|
|
* stack frame are valid.
|
|
|
|
*/
|
|
|
|
if ((pc | lr) != 0) {
|
|
|
|
unsigned long fnstart, fnend;
|
|
|
|
unsigned long nextip;
|
|
|
|
int printip = 1;
|
|
|
|
|
|
|
|
get_function_bounds(pc, &fnstart, &fnend);
|
|
|
|
nextip = 0;
|
|
|
|
if (newsp > sp)
|
2005-10-28 20:53:37 +08:00
|
|
|
mread(newsp + LRSAVE_OFFSET, &nextip,
|
2005-04-17 06:20:36 +08:00
|
|
|
sizeof(unsigned long));
|
|
|
|
if (lr == ip) {
|
|
|
|
if (lr < PAGE_OFFSET
|
|
|
|
|| (fnstart <= lr && lr < fnend))
|
|
|
|
printip = 0;
|
|
|
|
} else if (lr == nextip) {
|
|
|
|
printip = 0;
|
|
|
|
} else if (lr >= PAGE_OFFSET
|
|
|
|
&& !(fnstart <= lr && lr < fnend)) {
|
|
|
|
printf("[link register ] ");
|
|
|
|
xmon_print_symbol(lr, " ", "\n");
|
|
|
|
}
|
|
|
|
if (printip) {
|
2005-10-28 20:53:37 +08:00
|
|
|
printf("["REG"] ", sp);
|
2005-04-17 06:20:36 +08:00
|
|
|
xmon_print_symbol(ip, " ", " (unreliable)\n");
|
|
|
|
}
|
|
|
|
pc = lr = 0;
|
|
|
|
|
|
|
|
} else {
|
2005-10-28 20:53:37 +08:00
|
|
|
printf("["REG"] ", sp);
|
2005-04-17 06:20:36 +08:00
|
|
|
xmon_print_symbol(ip, " ", "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look for "regshere" marker to see if this is
|
|
|
|
an exception frame. */
|
2005-10-28 20:53:37 +08:00
|
|
|
if (mread(sp + MARKER_OFFSET, &marker, sizeof(unsigned long))
|
2008-04-17 12:34:59 +08:00
|
|
|
&& marker == STACK_FRAME_REGS_MARKER) {
|
2012-10-09 12:20:35 +08:00
|
|
|
if (mread(sp + STACK_FRAME_OVERHEAD, ®s, sizeof(regs))
|
2005-04-17 06:20:36 +08:00
|
|
|
!= sizeof(regs)) {
|
|
|
|
printf("Couldn't read registers at %lx\n",
|
2012-10-09 12:20:35 +08:00
|
|
|
sp + STACK_FRAME_OVERHEAD);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
2012-08-24 06:09:13 +08:00
|
|
|
printf("--- Exception: %lx %s at ", regs.trap,
|
2005-04-17 06:20:36 +08:00
|
|
|
getvecname(TRAP(®s)));
|
|
|
|
pc = regs.nip;
|
|
|
|
lr = regs.link;
|
|
|
|
xmon_print_symbol(pc, " ", "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newsp == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
sp = newsp;
|
2012-10-09 12:20:36 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void backtrace(struct pt_regs *excp)
|
|
|
|
{
|
|
|
|
unsigned long sp;
|
|
|
|
|
|
|
|
if (scanhex(&sp))
|
|
|
|
xmon_show_stack(sp, 0, 0);
|
|
|
|
else
|
|
|
|
xmon_show_stack(excp->gpr[1], excp->link, excp->nip);
|
|
|
|
scannl();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_bug_trap(struct pt_regs *regs)
|
|
|
|
{
|
2008-10-31 18:34:09 +08:00
|
|
|
#ifdef CONFIG_BUG
|
2006-12-08 19:30:41 +08:00
|
|
|
const struct bug_entry *bug;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long addr;
|
|
|
|
|
|
|
|
if (regs->msr & MSR_PR)
|
|
|
|
return; /* not in kernel */
|
|
|
|
addr = regs->nip; /* address of trap instruction */
|
|
|
|
if (addr < PAGE_OFFSET)
|
|
|
|
return;
|
|
|
|
bug = find_bug(regs->nip);
|
|
|
|
if (bug == NULL)
|
|
|
|
return;
|
2006-12-08 19:30:41 +08:00
|
|
|
if (is_warning_bug(bug))
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
|
2007-03-04 14:05:34 +08:00
|
|
|
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
2006-12-08 19:30:41 +08:00
|
|
|
printf("kernel BUG at %s:%u!\n",
|
|
|
|
bug->file, bug->line);
|
2007-03-04 14:05:34 +08:00
|
|
|
#else
|
|
|
|
printf("kernel BUG at %p!\n", (void *)bug->bug_addr);
|
|
|
|
#endif
|
2008-10-31 18:34:09 +08:00
|
|
|
#endif /* CONFIG_BUG */
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void excprint(struct pt_regs *fp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long trap;
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
printf("cpu 0x%x: ", smp_processor_id());
|
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
|
|
|
|
trap = TRAP(fp);
|
|
|
|
printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(trap), fp);
|
|
|
|
printf(" pc: ");
|
|
|
|
xmon_print_symbol(fp->nip, ": ", "\n");
|
|
|
|
|
|
|
|
printf(" lr: ", fp->link);
|
|
|
|
xmon_print_symbol(fp->link, ": ", "\n");
|
|
|
|
|
|
|
|
printf(" sp: %lx\n", fp->gpr[1]);
|
|
|
|
printf(" msr: %lx\n", fp->msr);
|
|
|
|
|
2013-04-28 17:37:26 +08:00
|
|
|
if (trap == 0x300 || trap == 0x380 || trap == 0x600 || trap == 0x200) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printf(" dar: %lx\n", fp->dar);
|
|
|
|
if (trap != 0x380)
|
|
|
|
printf(" dsisr: %lx\n", fp->dsisr);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(" current = 0x%lx\n", current);
|
2005-10-28 20:53:37 +08:00
|
|
|
#ifdef CONFIG_PPC64
|
powerpc: Rework lazy-interrupt handling
The current implementation of lazy interrupts handling has some
issues that this tries to address.
We don't do the various workarounds we need to do when re-enabling
interrupts in some cases such as when returning from an interrupt
and thus we may still lose or get delayed decrementer or doorbell
interrupts.
The current scheme also makes it much harder to handle the external
"edge" interrupts provided by some BookE processors when using the
EPR facility (External Proxy) and the Freescale Hypervisor.
Additionally, we tend to keep interrupts hard disabled in a number
of cases, such as decrementer interrupts, external interrupts, or
when a masked decrementer interrupt is pending. This is sub-optimal.
This is an attempt at fixing it all in one go by reworking the way
we do the lazy interrupt disabling from the ground up.
The base idea is to replace the "hard_enabled" field with a
"irq_happened" field in which we store a bit mask of what interrupt
occurred while soft-disabled.
When re-enabling, either via arch_local_irq_restore() or when returning
from an interrupt, we can now decide what to do by testing bits in that
field.
We then implement replaying of the missed interrupts either by
re-using the existing exception frame (in exception exit case) or via
the creation of a new one from an assembly trampoline (in the
arch_local_irq_enable case).
This removes the need to play with the decrementer to try to create
fake interrupts, among others.
In addition, this adds a few refinements:
- We no longer hard disable decrementer interrupts that occur
while soft-disabled. We now simply bump the decrementer back to max
(on BookS) or leave it stopped (on BookE) and continue with hard interrupts
enabled, which means that we'll potentially get better sample quality from
performance monitor interrupts.
- Timer, decrementer and doorbell interrupts now hard-enable
shortly after removing the source of the interrupt, which means
they no longer run entirely hard disabled. Again, this will improve
perf sample quality.
- On Book3E 64-bit, we now make the performance monitor interrupt
act as an NMI like Book3S (the necessary C code for that to work
appear to already be present in the FSL perf code, notably calling
nmi_enter instead of irq_enter). (This also fixes a bug where BookE
perfmon interrupts could clobber r14 ... oops)
- We could make "masked" decrementer interrupts act as NMIs when doing
timer-based perf sampling to improve the sample quality.
Signed-off-by-yet: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
v2:
- Add hard-enable to decrementer, timer and doorbells
- Fix CR clobber in masked irq handling on BookE
- Make embedded perf interrupt act as an NMI
- Add a PACA_HAPPENED_EE_EDGE for use by FSL if they want
to retrigger an interrupt without preventing hard-enable
v3:
- Fix or vs. ori bug on Book3E
- Fix enabling of interrupts for some exceptions on Book3E
v4:
- Fix resend of doorbells on return from interrupt on Book3E
v5:
- Rebased on top of my latest series, which involves some significant
rework of some aspects of the patch.
v6:
- 32-bit compile fix
- more compile fixes with various .config combos
- factor out the asm code to soft-disable interrupts
- remove the C wrapper around preempt_schedule_irq
v7:
- Fix a bug with hard irq state tracking on native power7
2012-03-06 15:27:59 +08:00
|
|
|
printf(" paca = 0x%lx\t softe: %d\t irq_happened: 0x%02x\n",
|
|
|
|
local_paca, local_paca->soft_enabled, local_paca->irq_happened);
|
2005-10-28 20:53:37 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
if (current) {
|
|
|
|
printf(" pid = %ld, comm = %s\n",
|
|
|
|
current->pid, current->comm);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trap == 0x700)
|
|
|
|
print_bug_trap(fp);
|
2015-11-25 10:46:25 +08:00
|
|
|
|
|
|
|
printf(linux_banner);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void prregs(struct pt_regs *fp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-10-28 20:53:37 +08:00
|
|
|
int n, trap;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long base;
|
|
|
|
struct pt_regs regs;
|
|
|
|
|
|
|
|
if (scanhex(&base)) {
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
regs = *(struct pt_regs *)base;
|
|
|
|
sync();
|
|
|
|
__delay(200);
|
|
|
|
} else {
|
|
|
|
catch_memory_errors = 0;
|
2005-10-28 20:53:37 +08:00
|
|
|
printf("*** Error reading registers from "REG"\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
base);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
fp = ®s;
|
|
|
|
}
|
|
|
|
|
2005-10-28 20:53:37 +08:00
|
|
|
#ifdef CONFIG_PPC64
|
2005-04-17 06:20:36 +08:00
|
|
|
if (FULL_REGS(fp)) {
|
|
|
|
for (n = 0; n < 16; ++n)
|
2005-10-28 20:53:37 +08:00
|
|
|
printf("R%.2ld = "REG" R%.2ld = "REG"\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
n, fp->gpr[n], n+16, fp->gpr[n+16]);
|
|
|
|
} else {
|
|
|
|
for (n = 0; n < 7; ++n)
|
2005-10-28 20:53:37 +08:00
|
|
|
printf("R%.2ld = "REG" R%.2ld = "REG"\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
n, fp->gpr[n], n+7, fp->gpr[n+7]);
|
|
|
|
}
|
2005-10-28 20:53:37 +08:00
|
|
|
#else
|
|
|
|
for (n = 0; n < 32; ++n) {
|
|
|
|
printf("R%.2d = %.8x%s", n, fp->gpr[n],
|
|
|
|
(n & 3) == 3? "\n": " ");
|
|
|
|
if (n == 12 && !FULL_REGS(fp)) {
|
|
|
|
printf("\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("pc = ");
|
|
|
|
xmon_print_symbol(fp->nip, " ", "\n");
|
2011-05-02 03:48:20 +08:00
|
|
|
if (TRAP(fp) != 0xc00 && cpu_has_feature(CPU_FTR_CFAR)) {
|
|
|
|
printf("cfar= ");
|
|
|
|
xmon_print_symbol(fp->orig_gpr3, " ", "\n");
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("lr = ");
|
|
|
|
xmon_print_symbol(fp->link, " ", "\n");
|
2005-10-28 20:53:37 +08:00
|
|
|
printf("msr = "REG" cr = %.8lx\n", fp->msr, fp->ccr);
|
|
|
|
printf("ctr = "REG" xer = "REG" trap = %4lx\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
fp->ctr, fp->xer, fp->trap);
|
2005-10-28 20:53:37 +08:00
|
|
|
trap = TRAP(fp);
|
|
|
|
if (trap == 0x300 || trap == 0x380 || trap == 0x600)
|
|
|
|
printf("dar = "REG" dsisr = %.8lx\n", fp->dar, fp->dsisr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void cacheflush(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int cmd;
|
|
|
|
unsigned long nflush;
|
|
|
|
|
|
|
|
cmd = inchar();
|
|
|
|
if (cmd != 'i')
|
|
|
|
termch = cmd;
|
|
|
|
scanhex((void *)&adrs);
|
|
|
|
if (termch != '\n')
|
|
|
|
termch = 0;
|
|
|
|
nflush = 1;
|
|
|
|
scanhex(&nflush);
|
|
|
|
nflush = (nflush + L1_CACHE_BYTES - 1) / L1_CACHE_BYTES;
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
|
|
|
|
if (cmd != 'i') {
|
|
|
|
for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
|
|
|
|
cflush((void *) adrs);
|
|
|
|
} else {
|
|
|
|
for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES)
|
|
|
|
cinval((void *) adrs);
|
|
|
|
}
|
|
|
|
sync();
|
|
|
|
/* wait a little while to see if we get a machine check */
|
|
|
|
__delay(200);
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static unsigned long
|
2005-04-17 06:20:36 +08:00
|
|
|
read_spr(int n)
|
|
|
|
{
|
|
|
|
unsigned int instrs[2];
|
|
|
|
unsigned long (*code)(void);
|
|
|
|
unsigned long ret = -1UL;
|
2005-11-11 19:36:34 +08:00
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
unsigned long opd[3];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
opd[0] = (unsigned long)instrs;
|
|
|
|
opd[1] = 0;
|
|
|
|
opd[2] = 0;
|
2005-11-11 19:36:34 +08:00
|
|
|
code = (unsigned long (*)(void)) opd;
|
|
|
|
#else
|
|
|
|
code = (unsigned long (*)(void)) instrs;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* mfspr r3,n; blr */
|
|
|
|
instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
|
|
|
|
instrs[1] = 0x4e800020;
|
2005-04-17 06:20:36 +08:00
|
|
|
store_inst(instrs);
|
|
|
|
store_inst(instrs+1);
|
|
|
|
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
|
|
|
|
ret = code();
|
|
|
|
|
|
|
|
sync();
|
|
|
|
/* wait a little while to see if we get a machine check */
|
|
|
|
__delay(200);
|
|
|
|
n = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
write_spr(int n, unsigned long val)
|
|
|
|
{
|
|
|
|
unsigned int instrs[2];
|
|
|
|
unsigned long (*code)(unsigned long);
|
2005-11-11 19:36:34 +08:00
|
|
|
#ifdef CONFIG_PPC64
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long opd[3];
|
|
|
|
|
|
|
|
opd[0] = (unsigned long)instrs;
|
|
|
|
opd[1] = 0;
|
|
|
|
opd[2] = 0;
|
2005-11-11 19:36:34 +08:00
|
|
|
code = (unsigned long (*)(unsigned long)) opd;
|
|
|
|
#else
|
|
|
|
code = (unsigned long (*)(unsigned long)) instrs;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
|
|
|
|
instrs[1] = 0x4e800020;
|
2005-04-17 06:20:36 +08:00
|
|
|
store_inst(instrs);
|
|
|
|
store_inst(instrs+1);
|
|
|
|
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
|
|
|
|
code(val);
|
|
|
|
|
|
|
|
sync();
|
|
|
|
/* wait a little while to see if we get a machine check */
|
|
|
|
__delay(200);
|
|
|
|
n = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long regno;
|
|
|
|
extern char exc_prolog;
|
|
|
|
extern char dec_exc;
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void super_regs(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int cmd;
|
|
|
|
unsigned long val;
|
|
|
|
|
|
|
|
cmd = skipbl();
|
|
|
|
if (cmd == '\n') {
|
2012-08-24 06:09:13 +08:00
|
|
|
unsigned long sp, toc;
|
2005-04-17 06:20:36 +08:00
|
|
|
asm("mr %0,1" : "=r" (sp) :);
|
|
|
|
asm("mr %0,2" : "=r" (toc) :);
|
|
|
|
|
2005-10-28 20:53:37 +08:00
|
|
|
printf("msr = "REG" sprg0= "REG"\n",
|
|
|
|
mfmsr(), mfspr(SPRN_SPRG0));
|
|
|
|
printf("pvr = "REG" sprg1= "REG"\n",
|
2012-08-24 06:09:13 +08:00
|
|
|
mfspr(SPRN_PVR), mfspr(SPRN_SPRG1));
|
2005-10-28 20:53:37 +08:00
|
|
|
printf("dec = "REG" sprg2= "REG"\n",
|
|
|
|
mfspr(SPRN_DEC), mfspr(SPRN_SPRG2));
|
|
|
|
printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3));
|
|
|
|
printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scanhex(®no);
|
|
|
|
switch (cmd) {
|
|
|
|
case 'w':
|
|
|
|
val = read_spr(regno);
|
|
|
|
scanhex(&val);
|
|
|
|
write_spr(regno, val);
|
|
|
|
/* fall through */
|
|
|
|
case 'r':
|
|
|
|
printf("spr %lx = %lx\n", regno, read_spr(regno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
scannl();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stuff for reading and writing memory safely
|
|
|
|
*/
|
2008-05-08 12:27:16 +08:00
|
|
|
static int
|
2005-04-17 06:20:36 +08:00
|
|
|
mread(unsigned long adrs, void *buf, int size)
|
|
|
|
{
|
|
|
|
volatile int n;
|
|
|
|
char *p, *q;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
p = (char *)adrs;
|
|
|
|
q = (char *)buf;
|
|
|
|
switch (size) {
|
|
|
|
case 2:
|
2005-10-28 20:53:37 +08:00
|
|
|
*(u16 *)q = *(u16 *)p;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case 4:
|
2005-10-28 20:53:37 +08:00
|
|
|
*(u32 *)q = *(u32 *)p;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case 8:
|
2005-10-28 20:53:37 +08:00
|
|
|
*(u64 *)q = *(u64 *)p;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
for( ; n < size; ++n) {
|
|
|
|
*q++ = *p++;
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sync();
|
|
|
|
/* wait a little while to see if we get a machine check */
|
|
|
|
__delay(200);
|
|
|
|
n = size;
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static int
|
2005-04-17 06:20:36 +08:00
|
|
|
mwrite(unsigned long adrs, void *buf, int size)
|
|
|
|
{
|
|
|
|
volatile int n;
|
|
|
|
char *p, *q;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
p = (char *) adrs;
|
|
|
|
q = (char *) buf;
|
|
|
|
switch (size) {
|
|
|
|
case 2:
|
2005-10-28 20:53:37 +08:00
|
|
|
*(u16 *)p = *(u16 *)q;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case 4:
|
2005-10-28 20:53:37 +08:00
|
|
|
*(u32 *)p = *(u32 *)q;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case 8:
|
2005-10-28 20:53:37 +08:00
|
|
|
*(u64 *)p = *(u64 *)q;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
for ( ; n < size; ++n) {
|
|
|
|
*p++ = *q++;
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sync();
|
|
|
|
/* wait a little while to see if we get a machine check */
|
|
|
|
__delay(200);
|
|
|
|
n = size;
|
|
|
|
} else {
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("*** Error writing address "REG"\n", adrs + n);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fault_type;
|
2005-10-28 20:53:37 +08:00
|
|
|
static int fault_except;
|
2005-04-17 06:20:36 +08:00
|
|
|
static char *fault_chars[] = { "--", "**", "##" };
|
|
|
|
|
2005-10-28 20:53:37 +08:00
|
|
|
static int handle_fault(struct pt_regs *regs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-10-28 20:53:37 +08:00
|
|
|
fault_except = TRAP(regs);
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (TRAP(regs)) {
|
|
|
|
case 0x200:
|
|
|
|
fault_type = 0;
|
|
|
|
break;
|
|
|
|
case 0x300:
|
|
|
|
case 0x380:
|
|
|
|
fault_type = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fault_type = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
longjmp(bus_error_jmp, 1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
byterev(unsigned char *val, int size)
|
|
|
|
{
|
|
|
|
int t;
|
|
|
|
|
|
|
|
switch (size) {
|
|
|
|
case 2:
|
|
|
|
SWAP(val[0], val[1], t);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
SWAP(val[0], val[3], t);
|
|
|
|
SWAP(val[1], val[2], t);
|
|
|
|
break;
|
|
|
|
case 8: /* is there really any use for this? */
|
|
|
|
SWAP(val[0], val[7], t);
|
|
|
|
SWAP(val[1], val[6], t);
|
|
|
|
SWAP(val[2], val[5], t);
|
|
|
|
SWAP(val[3], val[4], t);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int brev;
|
|
|
|
static int mnoread;
|
|
|
|
|
2012-08-24 06:09:13 +08:00
|
|
|
static char *memex_help_string =
|
2005-04-17 06:20:36 +08:00
|
|
|
"Memory examine command usage:\n"
|
|
|
|
"m [addr] [flags] examine/change memory\n"
|
|
|
|
" addr is optional. will start where left off.\n"
|
|
|
|
" flags may include chars from this set:\n"
|
|
|
|
" b modify by bytes (default)\n"
|
|
|
|
" w modify by words (2 byte)\n"
|
|
|
|
" l modify by longs (4 byte)\n"
|
|
|
|
" d modify by doubleword (8 byte)\n"
|
|
|
|
" r toggle reverse byte order mode\n"
|
|
|
|
" n do not read memory (for i/o spaces)\n"
|
|
|
|
" . ok to read (default)\n"
|
|
|
|
"NOTE: flags are saved as defaults\n"
|
|
|
|
"";
|
|
|
|
|
2012-08-24 06:09:13 +08:00
|
|
|
static char *memex_subcmd_help_string =
|
2005-04-17 06:20:36 +08:00
|
|
|
"Memory examine subcommands:\n"
|
|
|
|
" hexval write this val to current location\n"
|
|
|
|
" 'string' write chars from string to this location\n"
|
|
|
|
" ' increment address\n"
|
|
|
|
" ^ decrement address\n"
|
|
|
|
" / increment addr by 0x10. //=0x100, ///=0x1000, etc\n"
|
|
|
|
" \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n"
|
|
|
|
" ` clear no-read flag\n"
|
|
|
|
" ; stay at this addr\n"
|
|
|
|
" v change to byte mode\n"
|
|
|
|
" w change to word (2 byte) mode\n"
|
|
|
|
" l change to long (4 byte) mode\n"
|
|
|
|
" u change to doubleword (8 byte) mode\n"
|
|
|
|
" m addr change current addr\n"
|
|
|
|
" n toggle no-read flag\n"
|
|
|
|
" r toggle byte reverse flag\n"
|
|
|
|
" < count back up count bytes\n"
|
|
|
|
" > count skip forward count bytes\n"
|
|
|
|
" x exit this mode\n"
|
|
|
|
"";
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
memex(void)
|
|
|
|
{
|
|
|
|
int cmd, inc, i, nslash;
|
|
|
|
unsigned long n;
|
|
|
|
unsigned char val[16];
|
|
|
|
|
|
|
|
scanhex((void *)&adrs);
|
|
|
|
cmd = skipbl();
|
|
|
|
if (cmd == '?') {
|
|
|
|
printf(memex_help_string);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
termch = cmd;
|
|
|
|
}
|
|
|
|
last_cmd = "m\n";
|
|
|
|
while ((cmd = skipbl()) != '\n') {
|
|
|
|
switch( cmd ){
|
|
|
|
case 'b': size = 1; break;
|
|
|
|
case 'w': size = 2; break;
|
|
|
|
case 'l': size = 4; break;
|
|
|
|
case 'd': size = 8; break;
|
|
|
|
case 'r': brev = !brev; break;
|
|
|
|
case 'n': mnoread = 1; break;
|
|
|
|
case '.': mnoread = 0; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( size <= 0 )
|
|
|
|
size = 1;
|
|
|
|
else if( size > 8 )
|
|
|
|
size = 8;
|
|
|
|
for(;;){
|
|
|
|
if (!mnoread)
|
|
|
|
n = mread(adrs, val, size);
|
2005-11-10 11:30:20 +08:00
|
|
|
printf(REG"%c", adrs, brev? 'r': ' ');
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!mnoread) {
|
|
|
|
if (brev)
|
|
|
|
byterev(val, size);
|
|
|
|
putchar(' ');
|
|
|
|
for (i = 0; i < n; ++i)
|
|
|
|
printf("%.2x", val[i]);
|
|
|
|
for (; i < size; ++i)
|
|
|
|
printf("%s", fault_chars[fault_type]);
|
|
|
|
}
|
|
|
|
putchar(' ');
|
|
|
|
inc = size;
|
|
|
|
nslash = 0;
|
|
|
|
for(;;){
|
|
|
|
if( scanhex(&n) ){
|
|
|
|
for (i = 0; i < size; ++i)
|
|
|
|
val[i] = n >> (i * 8);
|
|
|
|
if (!brev)
|
|
|
|
byterev(val, size);
|
|
|
|
mwrite(adrs, val, size);
|
|
|
|
inc = size;
|
|
|
|
}
|
|
|
|
cmd = skipbl();
|
|
|
|
if (cmd == '\n')
|
|
|
|
break;
|
|
|
|
inc = 0;
|
|
|
|
switch (cmd) {
|
|
|
|
case '\'':
|
|
|
|
for(;;){
|
|
|
|
n = inchar();
|
|
|
|
if( n == '\\' )
|
|
|
|
n = bsesc();
|
|
|
|
else if( n == '\'' )
|
|
|
|
break;
|
|
|
|
for (i = 0; i < size; ++i)
|
|
|
|
val[i] = n >> (i * 8);
|
|
|
|
if (!brev)
|
|
|
|
byterev(val, size);
|
|
|
|
mwrite(adrs, val, size);
|
|
|
|
adrs += size;
|
|
|
|
}
|
|
|
|
adrs -= size;
|
|
|
|
inc = size;
|
|
|
|
break;
|
|
|
|
case ',':
|
|
|
|
adrs += size;
|
|
|
|
break;
|
|
|
|
case '.':
|
|
|
|
mnoread = 0;
|
|
|
|
break;
|
|
|
|
case ';':
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
case EOF:
|
|
|
|
scannl();
|
|
|
|
return;
|
|
|
|
case 'b':
|
|
|
|
case 'v':
|
|
|
|
size = 1;
|
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
size = 2;
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
size = 4;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
size = 8;
|
|
|
|
break;
|
|
|
|
case '^':
|
|
|
|
adrs -= size;
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
if (nslash > 0)
|
|
|
|
adrs -= 1 << nslash;
|
|
|
|
else
|
|
|
|
nslash = 0;
|
|
|
|
nslash += 4;
|
|
|
|
adrs += 1 << nslash;
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
if (nslash < 0)
|
|
|
|
adrs += 1 << -nslash;
|
|
|
|
else
|
|
|
|
nslash = 0;
|
|
|
|
nslash -= 4;
|
|
|
|
adrs -= 1 << -nslash;
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
scanhex((void *)&adrs);
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
mnoread = 1;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
brev = !brev;
|
|
|
|
break;
|
|
|
|
case '<':
|
|
|
|
n = size;
|
|
|
|
scanhex(&n);
|
|
|
|
adrs -= n;
|
|
|
|
break;
|
|
|
|
case '>':
|
|
|
|
n = size;
|
|
|
|
scanhex(&n);
|
|
|
|
adrs += n;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
printf(memex_subcmd_help_string);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
adrs += inc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static int
|
2005-04-17 06:20:36 +08:00
|
|
|
bsesc(void)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
c = inchar();
|
|
|
|
switch( c ){
|
|
|
|
case 'n': c = '\n'; break;
|
|
|
|
case 'r': c = '\r'; break;
|
|
|
|
case 'b': c = '\b'; break;
|
|
|
|
case 't': c = '\t'; break;
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2006-03-09 03:40:28 +08:00
|
|
|
static void xmon_rawdump (unsigned long adrs, long ndump)
|
|
|
|
{
|
|
|
|
long n, m, r, nr;
|
|
|
|
unsigned char temp[16];
|
|
|
|
|
|
|
|
for (n = ndump; n > 0;) {
|
|
|
|
r = n < 16? n: 16;
|
|
|
|
nr = mread(adrs, temp, r);
|
|
|
|
adrs += nr;
|
|
|
|
for (m = 0; m < r; ++m) {
|
|
|
|
if (m < nr)
|
|
|
|
printf("%.2x", temp[m]);
|
|
|
|
else
|
|
|
|
printf("%s", fault_chars[fault_type]);
|
|
|
|
}
|
|
|
|
n -= r;
|
|
|
|
if (nr < r)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2012-09-14 07:01:31 +08:00
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
static void dump_one_paca(int cpu)
|
|
|
|
{
|
|
|
|
struct paca_struct *p;
|
2015-10-14 13:58:36 +08:00
|
|
|
#ifdef CONFIG_PPC_STD_MMU_64
|
|
|
|
int i = 0;
|
|
|
|
#endif
|
2012-09-14 07:01:31 +08:00
|
|
|
|
|
|
|
if (setjmp(bus_error_jmp) != 0) {
|
|
|
|
printf("*** Error dumping paca for cpu 0x%x!\n", cpu);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
|
|
|
|
p = &paca[cpu];
|
|
|
|
|
|
|
|
printf("paca for cpu 0x%x @ %p:\n", cpu, p);
|
|
|
|
|
2015-10-14 13:58:36 +08:00
|
|
|
printf(" %-*s = %s\n", 20, "possible", cpu_possible(cpu) ? "yes" : "no");
|
|
|
|
printf(" %-*s = %s\n", 20, "present", cpu_present(cpu) ? "yes" : "no");
|
|
|
|
printf(" %-*s = %s\n", 20, "online", cpu_online(cpu) ? "yes" : "no");
|
2012-09-14 07:01:31 +08:00
|
|
|
|
|
|
|
#define DUMP(paca, name, format) \
|
2015-10-14 13:58:36 +08:00
|
|
|
printf(" %-*s = %#-*"format"\t(0x%lx)\n", 20, #name, 18, paca->name, \
|
2012-09-14 07:01:31 +08:00
|
|
|
offsetof(struct paca_struct, name));
|
|
|
|
|
|
|
|
DUMP(p, lock_token, "x");
|
|
|
|
DUMP(p, paca_index, "x");
|
|
|
|
DUMP(p, kernel_toc, "lx");
|
|
|
|
DUMP(p, kernelbase, "lx");
|
|
|
|
DUMP(p, kernel_msr, "lx");
|
|
|
|
DUMP(p, emergency_sp, "p");
|
2013-10-30 22:34:00 +08:00
|
|
|
#ifdef CONFIG_PPC_BOOK3S_64
|
|
|
|
DUMP(p, mc_emergency_sp, "p");
|
|
|
|
DUMP(p, in_mce, "x");
|
2015-10-14 13:58:36 +08:00
|
|
|
DUMP(p, hmi_event_available, "x");
|
2013-10-30 22:34:00 +08:00
|
|
|
#endif
|
2012-09-14 07:01:31 +08:00
|
|
|
DUMP(p, data_offset, "lx");
|
|
|
|
DUMP(p, hw_cpu_id, "x");
|
|
|
|
DUMP(p, cpu_start, "x");
|
|
|
|
DUMP(p, kexec_state, "x");
|
2015-10-14 13:58:36 +08:00
|
|
|
#ifdef CONFIG_PPC_STD_MMU_64
|
|
|
|
for (i = 0; i < SLB_NUM_BOLTED; i++) {
|
|
|
|
u64 esid, vsid;
|
|
|
|
|
|
|
|
if (!p->slb_shadow_ptr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
esid = be64_to_cpu(p->slb_shadow_ptr->save_area[i].esid);
|
|
|
|
vsid = be64_to_cpu(p->slb_shadow_ptr->save_area[i].vsid);
|
|
|
|
|
|
|
|
if (esid || vsid) {
|
|
|
|
printf(" slb_shadow[%d]: = 0x%016lx 0x%016lx\n",
|
|
|
|
i, esid, vsid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DUMP(p, vmalloc_sllp, "x");
|
|
|
|
DUMP(p, slb_cache_ptr, "x");
|
|
|
|
for (i = 0; i < SLB_CACHE_ENTRIES; i++)
|
|
|
|
printf(" slb_cache[%d]: = 0x%016lx\n", i, p->slb_cache[i]);
|
|
|
|
#endif
|
|
|
|
DUMP(p, dscr_default, "llx");
|
|
|
|
#ifdef CONFIG_PPC_BOOK3E
|
|
|
|
DUMP(p, pgd, "p");
|
|
|
|
DUMP(p, kernel_pgd, "p");
|
|
|
|
DUMP(p, tcd_ptr, "p");
|
|
|
|
DUMP(p, mc_kstack, "p");
|
|
|
|
DUMP(p, crit_kstack, "p");
|
|
|
|
DUMP(p, dbg_kstack, "p");
|
|
|
|
#endif
|
2012-09-14 07:01:31 +08:00
|
|
|
DUMP(p, __current, "p");
|
|
|
|
DUMP(p, kstack, "lx");
|
|
|
|
DUMP(p, stab_rr, "lx");
|
|
|
|
DUMP(p, saved_r1, "lx");
|
|
|
|
DUMP(p, trap_save, "x");
|
|
|
|
DUMP(p, soft_enabled, "x");
|
|
|
|
DUMP(p, irq_happened, "x");
|
|
|
|
DUMP(p, io_sync, "x");
|
|
|
|
DUMP(p, irq_work_pending, "x");
|
|
|
|
DUMP(p, nap_state_lost, "x");
|
2015-10-14 13:58:36 +08:00
|
|
|
DUMP(p, sprg_vdso, "llx");
|
|
|
|
|
|
|
|
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
|
|
|
|
DUMP(p, tm_scratch, "llx");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_PPC_POWERNV
|
|
|
|
DUMP(p, core_idle_state_ptr, "p");
|
|
|
|
DUMP(p, thread_idle_state, "x");
|
|
|
|
DUMP(p, thread_mask, "x");
|
|
|
|
DUMP(p, subcore_sibling_mask, "x");
|
|
|
|
#endif
|
2012-09-14 07:01:31 +08:00
|
|
|
|
2015-10-14 13:58:36 +08:00
|
|
|
DUMP(p, user_time, "llx");
|
|
|
|
DUMP(p, system_time, "llx");
|
|
|
|
DUMP(p, user_time_scaled, "llx");
|
|
|
|
DUMP(p, starttime, "llx");
|
|
|
|
DUMP(p, starttime_user, "llx");
|
|
|
|
DUMP(p, startspurr, "llx");
|
|
|
|
DUMP(p, utime_sspurr, "llx");
|
|
|
|
DUMP(p, stolen_time, "llx");
|
2012-09-14 07:01:31 +08:00
|
|
|
#undef DUMP
|
|
|
|
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_all_pacas(void)
|
|
|
|
{
|
|
|
|
int cpu;
|
|
|
|
|
|
|
|
if (num_possible_cpus() == 0) {
|
|
|
|
printf("No possible cpus, use 'dp #' to dump individual cpus\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu)
|
|
|
|
dump_one_paca(cpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_pacas(void)
|
|
|
|
{
|
|
|
|
unsigned long num;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
c = inchar();
|
|
|
|
if (c == 'a') {
|
|
|
|
dump_all_pacas();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
termch = c; /* Put c back, it wasn't 'a' */
|
|
|
|
|
|
|
|
if (scanhex(&num))
|
|
|
|
dump_one_paca(num);
|
|
|
|
else
|
|
|
|
dump_one_paca(xmon_owner);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
dump(void)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
c = inchar();
|
2012-09-14 07:01:31 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
if (c == 'p') {
|
2015-10-08 08:50:23 +08:00
|
|
|
xmon_start_pagination();
|
2012-09-14 07:01:31 +08:00
|
|
|
dump_pacas();
|
2015-10-08 08:50:23 +08:00
|
|
|
xmon_end_pagination();
|
2012-09-14 07:01:31 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n')
|
|
|
|
termch = c;
|
|
|
|
scanhex((void *)&adrs);
|
|
|
|
if (termch != '\n')
|
|
|
|
termch = 0;
|
|
|
|
if (c == 'i') {
|
|
|
|
scanhex(&nidump);
|
|
|
|
if (nidump == 0)
|
|
|
|
nidump = 16;
|
|
|
|
else if (nidump > MAX_DUMP)
|
|
|
|
nidump = MAX_DUMP;
|
|
|
|
adrs += ppc_inst_dump(adrs, nidump, 1);
|
|
|
|
last_cmd = "di\n";
|
2009-05-15 07:13:07 +08:00
|
|
|
} else if (c == 'l') {
|
|
|
|
dump_log_buf();
|
2016-02-09 15:17:49 +08:00
|
|
|
} else if (c == 'o') {
|
|
|
|
dump_opal_msglog();
|
2006-03-09 03:40:28 +08:00
|
|
|
} else if (c == 'r') {
|
|
|
|
scanhex(&ndump);
|
|
|
|
if (ndump == 0)
|
|
|
|
ndump = 64;
|
|
|
|
xmon_rawdump(adrs, ndump);
|
|
|
|
adrs += ndump;
|
|
|
|
last_cmd = "dr\n";
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
scanhex(&ndump);
|
|
|
|
if (ndump == 0)
|
|
|
|
ndump = 64;
|
|
|
|
else if (ndump > MAX_DUMP)
|
|
|
|
ndump = MAX_DUMP;
|
|
|
|
prdump(adrs, ndump);
|
|
|
|
adrs += ndump;
|
|
|
|
last_cmd = "d\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
prdump(unsigned long adrs, long ndump)
|
|
|
|
{
|
|
|
|
long n, m, c, r, nr;
|
|
|
|
unsigned char temp[16];
|
|
|
|
|
|
|
|
for (n = ndump; n > 0;) {
|
2005-10-28 20:53:37 +08:00
|
|
|
printf(REG, adrs);
|
2005-04-17 06:20:36 +08:00
|
|
|
putchar(' ');
|
|
|
|
r = n < 16? n: 16;
|
|
|
|
nr = mread(adrs, temp, r);
|
|
|
|
adrs += nr;
|
|
|
|
for (m = 0; m < r; ++m) {
|
2012-08-24 06:09:13 +08:00
|
|
|
if ((m & (sizeof(long) - 1)) == 0 && m > 0)
|
2005-11-10 11:30:20 +08:00
|
|
|
putchar(' ');
|
2005-04-17 06:20:36 +08:00
|
|
|
if (m < nr)
|
|
|
|
printf("%.2x", temp[m]);
|
|
|
|
else
|
|
|
|
printf("%s", fault_chars[fault_type]);
|
|
|
|
}
|
2005-11-10 11:30:20 +08:00
|
|
|
for (; m < 16; ++m) {
|
2012-08-24 06:09:13 +08:00
|
|
|
if ((m & (sizeof(long) - 1)) == 0)
|
2005-11-10 11:30:20 +08:00
|
|
|
putchar(' ');
|
2005-04-17 06:20:36 +08:00
|
|
|
printf(" ");
|
2005-11-10 11:30:20 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
printf(" |");
|
|
|
|
for (m = 0; m < r; ++m) {
|
|
|
|
if (m < nr) {
|
|
|
|
c = temp[m];
|
|
|
|
putchar(' ' <= c && c <= '~'? c: '.');
|
|
|
|
} else
|
|
|
|
putchar(' ');
|
|
|
|
}
|
|
|
|
n -= r;
|
|
|
|
for (; m < 16; ++m)
|
|
|
|
putchar(' ');
|
|
|
|
printf("|\n");
|
|
|
|
if (nr < r)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-23 07:46:42 +08:00
|
|
|
typedef int (*instruction_dump_func)(unsigned long inst, unsigned long addr);
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static int
|
2006-11-23 07:46:42 +08:00
|
|
|
generic_inst_dump(unsigned long adr, long count, int praddr,
|
|
|
|
instruction_dump_func dump_func)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int nr, dotted;
|
|
|
|
unsigned long first_adr;
|
|
|
|
unsigned long inst, last_inst = 0;
|
|
|
|
unsigned char val[4];
|
|
|
|
|
|
|
|
dotted = 0;
|
|
|
|
for (first_adr = adr; count > 0; --count, adr += 4) {
|
|
|
|
nr = mread(adr, val, 4);
|
|
|
|
if (nr == 0) {
|
|
|
|
if (praddr) {
|
|
|
|
const char *x = fault_chars[fault_type];
|
2005-10-28 20:53:37 +08:00
|
|
|
printf(REG" %s%s%s%s\n", adr, x, x, x, x);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
inst = GETWORD(val);
|
|
|
|
if (adr > first_adr && inst == last_inst) {
|
|
|
|
if (!dotted) {
|
|
|
|
printf(" ...\n");
|
|
|
|
dotted = 1;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
dotted = 0;
|
|
|
|
last_inst = inst;
|
|
|
|
if (praddr)
|
2005-10-28 20:53:37 +08:00
|
|
|
printf(REG" %.8x", adr, inst);
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("\t");
|
2006-11-23 07:46:42 +08:00
|
|
|
dump_func(inst, adr);
|
2005-04-17 06:20:36 +08:00
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
return adr - first_adr;
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static int
|
2006-11-23 07:46:42 +08:00
|
|
|
ppc_inst_dump(unsigned long adr, long count, int praddr)
|
|
|
|
{
|
|
|
|
return generic_inst_dump(adr, count, praddr, print_insn_powerpc);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
void
|
|
|
|
print_address(unsigned long addr)
|
|
|
|
{
|
|
|
|
xmon_print_symbol(addr, "\t# ", "");
|
|
|
|
}
|
|
|
|
|
2009-05-15 07:13:07 +08:00
|
|
|
void
|
|
|
|
dump_log_buf(void)
|
|
|
|
{
|
2012-08-24 06:09:12 +08:00
|
|
|
struct kmsg_dumper dumper = { .active = 1 };
|
|
|
|
unsigned char buf[128];
|
|
|
|
size_t len;
|
2009-05-15 07:13:07 +08:00
|
|
|
|
2012-08-24 06:09:13 +08:00
|
|
|
if (setjmp(bus_error_jmp) != 0) {
|
2012-08-24 06:09:12 +08:00
|
|
|
printf("Error dumping printk buffer!\n");
|
2012-08-24 06:09:13 +08:00
|
|
|
return;
|
|
|
|
}
|
2009-05-15 07:13:07 +08:00
|
|
|
|
2012-08-24 06:09:13 +08:00
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
2009-05-15 07:13:07 +08:00
|
|
|
|
2012-08-24 06:09:12 +08:00
|
|
|
kmsg_dump_rewind_nolock(&dumper);
|
2015-10-08 08:50:24 +08:00
|
|
|
xmon_start_pagination();
|
2012-08-24 06:09:12 +08:00
|
|
|
while (kmsg_dump_get_line_nolock(&dumper, false, buf, sizeof(buf), &len)) {
|
|
|
|
buf[len] = '\0';
|
|
|
|
printf("%s", buf);
|
|
|
|
}
|
2015-10-08 08:50:24 +08:00
|
|
|
xmon_end_pagination();
|
2009-05-15 07:13:07 +08:00
|
|
|
|
2012-08-24 06:09:13 +08:00
|
|
|
sync();
|
|
|
|
/* wait a little while to see if we get a machine check */
|
|
|
|
__delay(200);
|
|
|
|
catch_memory_errors = 0;
|
2009-05-15 07:13:07 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-02-09 15:17:49 +08:00
|
|
|
#ifdef CONFIG_PPC_POWERNV
|
|
|
|
static void dump_opal_msglog(void)
|
|
|
|
{
|
|
|
|
unsigned char buf[128];
|
|
|
|
ssize_t res;
|
|
|
|
loff_t pos = 0;
|
|
|
|
|
|
|
|
if (!firmware_has_feature(FW_FEATURE_OPAL)) {
|
|
|
|
printf("Machine is not running OPAL firmware.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setjmp(bus_error_jmp) != 0) {
|
|
|
|
printf("Error dumping OPAL msglog!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
|
|
|
|
xmon_start_pagination();
|
|
|
|
while ((res = opal_msglog_copy(buf, pos, sizeof(buf) - 1))) {
|
|
|
|
if (res < 0) {
|
|
|
|
printf("Error dumping OPAL msglog! Error: %zd\n", res);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
buf[res] = '\0';
|
|
|
|
printf("%s", buf);
|
|
|
|
pos += res;
|
|
|
|
}
|
|
|
|
xmon_end_pagination();
|
|
|
|
|
|
|
|
sync();
|
|
|
|
/* wait a little while to see if we get a machine check */
|
|
|
|
__delay(200);
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Memory operations - move, set, print differences
|
|
|
|
*/
|
|
|
|
static unsigned long mdest; /* destination address */
|
|
|
|
static unsigned long msrc; /* source address */
|
|
|
|
static unsigned long mval; /* byte value to set memory to */
|
|
|
|
static unsigned long mcount; /* # bytes to affect */
|
|
|
|
static unsigned long mdiffs; /* max # differences to print */
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
memops(int cmd)
|
|
|
|
{
|
|
|
|
scanhex((void *)&mdest);
|
|
|
|
if( termch != '\n' )
|
|
|
|
termch = 0;
|
|
|
|
scanhex((void *)(cmd == 's'? &mval: &msrc));
|
|
|
|
if( termch != '\n' )
|
|
|
|
termch = 0;
|
|
|
|
scanhex((void *)&mcount);
|
|
|
|
switch( cmd ){
|
|
|
|
case 'm':
|
|
|
|
memmove((void *)mdest, (void *)msrc, mcount);
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
memset((void *)mdest, mval, mcount);
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
if( termch != '\n' )
|
|
|
|
termch = 0;
|
|
|
|
scanhex((void *)&mdiffs);
|
|
|
|
memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr)
|
|
|
|
{
|
|
|
|
unsigned n, prt;
|
|
|
|
|
|
|
|
prt = 0;
|
|
|
|
for( n = nb; n > 0; --n )
|
|
|
|
if( *p1++ != *p2++ )
|
|
|
|
if( ++prt <= maxpr )
|
|
|
|
printf("%.16x %.2x # %.16x %.2x\n", p1 - 1,
|
|
|
|
p1[-1], p2 - 1, p2[-1]);
|
|
|
|
if( prt > maxpr )
|
|
|
|
printf("Total of %d differences\n", prt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned mend;
|
|
|
|
static unsigned mask;
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
memlocate(void)
|
|
|
|
{
|
|
|
|
unsigned a, n;
|
|
|
|
unsigned char val[4];
|
|
|
|
|
|
|
|
last_cmd = "ml";
|
|
|
|
scanhex((void *)&mdest);
|
|
|
|
if (termch != '\n') {
|
|
|
|
termch = 0;
|
|
|
|
scanhex((void *)&mend);
|
|
|
|
if (termch != '\n') {
|
|
|
|
termch = 0;
|
|
|
|
scanhex((void *)&mval);
|
|
|
|
mask = ~0;
|
|
|
|
if (termch != '\n') termch = 0;
|
|
|
|
scanhex((void *)&mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
n = 0;
|
|
|
|
for (a = mdest; a < mend; a += 4) {
|
|
|
|
if (mread(a, val, 4) == 4
|
|
|
|
&& ((GETWORD(val) ^ mval) & mask) == 0) {
|
|
|
|
printf("%.16x: %.16x\n", a, GETWORD(val));
|
|
|
|
if (++n >= 10)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long mskip = 0x1000;
|
|
|
|
static unsigned long mlim = 0xffffffff;
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
memzcan(void)
|
|
|
|
{
|
|
|
|
unsigned char v;
|
|
|
|
unsigned a;
|
|
|
|
int ok, ook;
|
|
|
|
|
|
|
|
scanhex(&mdest);
|
|
|
|
if (termch != '\n') termch = 0;
|
|
|
|
scanhex(&mskip);
|
|
|
|
if (termch != '\n') termch = 0;
|
|
|
|
scanhex(&mlim);
|
|
|
|
ook = 0;
|
|
|
|
for (a = mdest; a < mlim; a += mskip) {
|
|
|
|
ok = mread(a, &v, 1);
|
|
|
|
if (ok && !ook) {
|
|
|
|
printf("%.8x .. ", a);
|
|
|
|
} else if (!ok && ook)
|
|
|
|
printf("%.8x\n", a - mskip);
|
|
|
|
ook = ok;
|
|
|
|
if (a + mskip < a)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ook)
|
|
|
|
printf("%.8x\n", a - mskip);
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void proccall(void)
|
2005-10-28 20:53:37 +08:00
|
|
|
{
|
|
|
|
unsigned long args[8];
|
|
|
|
unsigned long ret;
|
|
|
|
int i;
|
|
|
|
typedef unsigned long (*callfunc_t)(unsigned long, unsigned long,
|
|
|
|
unsigned long, unsigned long, unsigned long,
|
|
|
|
unsigned long, unsigned long, unsigned long);
|
|
|
|
callfunc_t func;
|
|
|
|
|
|
|
|
if (!scanhex(&adrs))
|
|
|
|
return;
|
|
|
|
if (termch != '\n')
|
|
|
|
termch = 0;
|
|
|
|
for (i = 0; i < 8; ++i)
|
|
|
|
args[i] = 0;
|
|
|
|
for (i = 0; i < 8; ++i) {
|
|
|
|
if (!scanhex(&args[i]) || termch == '\n')
|
|
|
|
break;
|
|
|
|
termch = 0;
|
|
|
|
}
|
|
|
|
func = (callfunc_t) adrs;
|
|
|
|
ret = 0;
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
ret = func(args[0], args[1], args[2], args[3],
|
|
|
|
args[4], args[5], args[6], args[7]);
|
|
|
|
sync();
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("return value is 0x%lx\n", ret);
|
2005-10-28 20:53:37 +08:00
|
|
|
} else {
|
|
|
|
printf("*** %x exception occurred\n", fault_except);
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Input scanning routines */
|
|
|
|
int
|
|
|
|
skipbl(void)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
if( termch != 0 ){
|
|
|
|
c = termch;
|
|
|
|
termch = 0;
|
|
|
|
} else
|
|
|
|
c = inchar();
|
|
|
|
while( c == ' ' || c == '\t' )
|
|
|
|
c = inchar();
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define N_PTREGS 44
|
|
|
|
static char *regnames[N_PTREGS] = {
|
|
|
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
|
|
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
|
|
|
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
|
|
|
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
|
2005-10-28 20:53:37 +08:00
|
|
|
"pc", "msr", "or3", "ctr", "lr", "xer", "ccr",
|
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
"softe",
|
|
|
|
#else
|
|
|
|
"mq",
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
"trap", "dar", "dsisr", "res"
|
|
|
|
};
|
|
|
|
|
|
|
|
int
|
|
|
|
scanhex(unsigned long *vp)
|
|
|
|
{
|
|
|
|
int c, d;
|
|
|
|
unsigned long v;
|
|
|
|
|
|
|
|
c = skipbl();
|
|
|
|
if (c == '%') {
|
|
|
|
/* parse register name */
|
|
|
|
char regname[8];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(regname) - 1; ++i) {
|
|
|
|
c = inchar();
|
|
|
|
if (!isalnum(c)) {
|
|
|
|
termch = c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
regname[i] = c;
|
|
|
|
}
|
|
|
|
regname[i] = 0;
|
|
|
|
for (i = 0; i < N_PTREGS; ++i) {
|
|
|
|
if (strcmp(regnames[i], regname) == 0) {
|
|
|
|
if (xmon_regs == NULL) {
|
|
|
|
printf("regs not available\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*vp = ((unsigned long *)xmon_regs)[i];
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("invalid register name '%%%s'\n", regname);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip leading "0x" if any */
|
|
|
|
|
|
|
|
if (c == '0') {
|
|
|
|
c = inchar();
|
|
|
|
if (c == 'x') {
|
|
|
|
c = inchar();
|
|
|
|
} else {
|
|
|
|
d = hexdigit(c);
|
|
|
|
if (d == EOF) {
|
|
|
|
termch = c;
|
|
|
|
*vp = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (c == '$') {
|
|
|
|
int i;
|
|
|
|
for (i=0; i<63; i++) {
|
|
|
|
c = inchar();
|
2014-07-15 19:43:47 +08:00
|
|
|
if (isspace(c) || c == '\0') {
|
2005-04-17 06:20:36 +08:00
|
|
|
termch = c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tmpstr[i] = c;
|
|
|
|
}
|
|
|
|
tmpstr[i++] = 0;
|
2005-06-22 08:15:30 +08:00
|
|
|
*vp = 0;
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
*vp = kallsyms_lookup_name(tmpstr);
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!(*vp)) {
|
|
|
|
printf("unknown symbol '%s'\n", tmpstr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
d = hexdigit(c);
|
|
|
|
if (d == EOF) {
|
|
|
|
termch = c;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
v = 0;
|
|
|
|
do {
|
|
|
|
v = (v << 4) + d;
|
|
|
|
c = inchar();
|
|
|
|
d = hexdigit(c);
|
|
|
|
} while (d != EOF);
|
|
|
|
termch = c;
|
|
|
|
*vp = v;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
scannl(void)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
c = termch;
|
|
|
|
termch = 0;
|
|
|
|
while( c != '\n' )
|
|
|
|
c = inchar();
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static int hexdigit(int c)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if( '0' <= c && c <= '9' )
|
|
|
|
return c - '0';
|
|
|
|
if( 'A' <= c && c <= 'F' )
|
|
|
|
return c - ('A' - 10);
|
|
|
|
if( 'a' <= c && c <= 'f' )
|
|
|
|
return c - ('a' - 10);
|
|
|
|
return EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
getstring(char *s, int size)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
c = skipbl();
|
|
|
|
do {
|
|
|
|
if( size > 1 ){
|
|
|
|
*s++ = c;
|
|
|
|
--size;
|
|
|
|
}
|
|
|
|
c = inchar();
|
|
|
|
} while( c != ' ' && c != '\t' && c != '\n' );
|
|
|
|
termch = c;
|
|
|
|
*s = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char line[256];
|
|
|
|
static char *lineptr;
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
flush_input(void)
|
|
|
|
{
|
|
|
|
lineptr = NULL;
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static int
|
2005-04-17 06:20:36 +08:00
|
|
|
inchar(void)
|
|
|
|
{
|
|
|
|
if (lineptr == NULL || *lineptr == 0) {
|
2005-11-08 19:55:08 +08:00
|
|
|
if (xmon_gets(line, sizeof(line)) == NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
lineptr = NULL;
|
|
|
|
return EOF;
|
|
|
|
}
|
|
|
|
lineptr = line;
|
|
|
|
}
|
|
|
|
return *lineptr++;
|
|
|
|
}
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void
|
2005-04-17 06:20:36 +08:00
|
|
|
take_input(char *str)
|
|
|
|
{
|
|
|
|
lineptr = str;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
symbol_lookup(void)
|
|
|
|
{
|
|
|
|
int type = inchar();
|
|
|
|
unsigned long addr;
|
|
|
|
static char tmp[64];
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case 'a':
|
|
|
|
if (scanhex(&addr))
|
|
|
|
xmon_print_symbol(addr, ": ", "\n");
|
|
|
|
termch = 0;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
getstring(tmp, 64);
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
addr = kallsyms_lookup_name(tmp);
|
|
|
|
if (addr)
|
|
|
|
printf("%s: %lx\n", tmp, addr);
|
|
|
|
else
|
|
|
|
printf("Symbol '%s' not found.\n", tmp);
|
|
|
|
sync();
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
termch = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Print an address in numeric and symbolic form (if possible) */
|
|
|
|
static void xmon_print_symbol(unsigned long address, const char *mid,
|
|
|
|
const char *after)
|
|
|
|
{
|
|
|
|
char *modname;
|
|
|
|
const char *name = NULL;
|
|
|
|
unsigned long offset, size;
|
|
|
|
|
2005-10-28 20:53:37 +08:00
|
|
|
printf(REG, address);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
name = kallsyms_lookup(address, &size, &offset, &modname,
|
|
|
|
tmpstr);
|
|
|
|
sync();
|
|
|
|
/* wait a little while to see if we get a machine check */
|
|
|
|
__delay(200);
|
|
|
|
}
|
|
|
|
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
printf("%s%s+%#lx/%#lx", mid, name, offset, size);
|
|
|
|
if (modname)
|
|
|
|
printf(" [%s]", modname);
|
|
|
|
}
|
|
|
|
printf("%s", after);
|
|
|
|
}
|
|
|
|
|
2009-07-24 07:15:59 +08:00
|
|
|
#ifdef CONFIG_PPC_BOOK3S_64
|
2014-07-10 10:29:20 +08:00
|
|
|
void dump_segments(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
2015-07-29 15:10:04 +08:00
|
|
|
unsigned long esid,vsid;
|
2007-12-07 05:22:23 +08:00
|
|
|
unsigned long llp;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-05-26 19:02:14 +08:00
|
|
|
printf("SLB contents of cpu 0x%x\n", smp_processor_id());
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-12-06 14:24:48 +08:00
|
|
|
for (i = 0; i < mmu_slb_size; i++) {
|
2007-12-07 05:22:23 +08:00
|
|
|
asm volatile("slbmfee %0,%1" : "=r" (esid) : "r" (i));
|
|
|
|
asm volatile("slbmfev %0,%1" : "=r" (vsid) : "r" (i));
|
2015-07-29 15:10:04 +08:00
|
|
|
if (esid || vsid) {
|
2007-12-07 05:22:23 +08:00
|
|
|
printf("%02d %016lx %016lx", i, esid, vsid);
|
2015-07-29 15:10:04 +08:00
|
|
|
if (esid & SLB_ESID_V) {
|
2007-12-07 05:22:23 +08:00
|
|
|
llp = vsid & SLB_VSID_LLP;
|
|
|
|
if (vsid & SLB_VSID_B_1T) {
|
|
|
|
printf(" 1T ESID=%9lx VSID=%13lx LLP:%3lx \n",
|
|
|
|
GET_ESID_1T(esid),
|
|
|
|
(vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT_1T,
|
|
|
|
llp);
|
|
|
|
} else {
|
|
|
|
printf(" 256M ESID=%9lx VSID=%13lx LLP:%3lx \n",
|
|
|
|
GET_ESID(esid),
|
|
|
|
(vsid & ~SLB_VSID_B) >> SLB_VSID_SHIFT,
|
|
|
|
llp);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
printf("\n");
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2005-10-28 20:53:37 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_PPC_STD_MMU_32
|
|
|
|
void dump_segments(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
printf("sr0-15 =");
|
|
|
|
for (i = 0; i < 16; ++i)
|
|
|
|
printf(" %x", mfsrin(i));
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-11-16 15:23:33 +08:00
|
|
|
#ifdef CONFIG_44x
|
|
|
|
static void dump_tlb_44x(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < PPC44x_TLB_SIZE; i++) {
|
|
|
|
unsigned long w0,w1,w2;
|
|
|
|
asm volatile("tlbre %0,%1,0" : "=r" (w0) : "r" (i));
|
|
|
|
asm volatile("tlbre %0,%1,1" : "=r" (w1) : "r" (i));
|
|
|
|
asm volatile("tlbre %0,%1,2" : "=r" (w2) : "r" (i));
|
|
|
|
printf("[%02x] %08x %08x %08x ", i, w0, w1, w2);
|
|
|
|
if (w0 & PPC44x_TLB_VALID) {
|
|
|
|
printf("V %08x -> %01x%08x %c%c%c%c%c",
|
|
|
|
w0 & PPC44x_TLB_EPN_MASK,
|
|
|
|
w1 & PPC44x_TLB_ERPN_MASK,
|
|
|
|
w1 & PPC44x_TLB_RPN_MASK,
|
|
|
|
(w2 & PPC44x_TLB_W) ? 'W' : 'w',
|
|
|
|
(w2 & PPC44x_TLB_I) ? 'I' : 'i',
|
|
|
|
(w2 & PPC44x_TLB_M) ? 'M' : 'm',
|
|
|
|
(w2 & PPC44x_TLB_G) ? 'G' : 'g',
|
|
|
|
(w2 & PPC44x_TLB_E) ? 'E' : 'e');
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_44x */
|
2008-05-08 12:27:16 +08:00
|
|
|
|
2010-07-09 13:34:50 +08:00
|
|
|
#ifdef CONFIG_PPC_BOOK3E
|
|
|
|
static void dump_tlb_book3e(void)
|
|
|
|
{
|
|
|
|
u32 mmucfg, pidmask, lpidmask;
|
|
|
|
u64 ramask;
|
|
|
|
int i, tlb, ntlbs, pidsz, lpidsz, rasz, lrat = 0;
|
|
|
|
int mmu_version;
|
|
|
|
static const char *pgsz_names[] = {
|
|
|
|
" 1K",
|
|
|
|
" 2K",
|
|
|
|
" 4K",
|
|
|
|
" 8K",
|
|
|
|
" 16K",
|
|
|
|
" 32K",
|
|
|
|
" 64K",
|
|
|
|
"128K",
|
|
|
|
"256K",
|
|
|
|
"512K",
|
|
|
|
" 1M",
|
|
|
|
" 2M",
|
|
|
|
" 4M",
|
|
|
|
" 8M",
|
|
|
|
" 16M",
|
|
|
|
" 32M",
|
|
|
|
" 64M",
|
|
|
|
"128M",
|
|
|
|
"256M",
|
|
|
|
"512M",
|
|
|
|
" 1G",
|
|
|
|
" 2G",
|
|
|
|
" 4G",
|
|
|
|
" 8G",
|
|
|
|
" 16G",
|
|
|
|
" 32G",
|
|
|
|
" 64G",
|
|
|
|
"128G",
|
|
|
|
"256G",
|
|
|
|
"512G",
|
|
|
|
" 1T",
|
|
|
|
" 2T",
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Gather some infos about the MMU */
|
|
|
|
mmucfg = mfspr(SPRN_MMUCFG);
|
|
|
|
mmu_version = (mmucfg & 3) + 1;
|
|
|
|
ntlbs = ((mmucfg >> 2) & 3) + 1;
|
|
|
|
pidsz = ((mmucfg >> 6) & 0x1f) + 1;
|
|
|
|
lpidsz = (mmucfg >> 24) & 0xf;
|
|
|
|
rasz = (mmucfg >> 16) & 0x7f;
|
|
|
|
if ((mmu_version > 1) && (mmucfg & 0x10000))
|
|
|
|
lrat = 1;
|
|
|
|
printf("Book3E MMU MAV=%d.0,%d TLBs,%d-bit PID,%d-bit LPID,%d-bit RA\n",
|
|
|
|
mmu_version, ntlbs, pidsz, lpidsz, rasz);
|
|
|
|
pidmask = (1ul << pidsz) - 1;
|
|
|
|
lpidmask = (1ul << lpidsz) - 1;
|
|
|
|
ramask = (1ull << rasz) - 1;
|
|
|
|
|
|
|
|
for (tlb = 0; tlb < ntlbs; tlb++) {
|
|
|
|
u32 tlbcfg;
|
|
|
|
int nent, assoc, new_cc = 1;
|
|
|
|
printf("TLB %d:\n------\n", tlb);
|
|
|
|
switch(tlb) {
|
|
|
|
case 0:
|
|
|
|
tlbcfg = mfspr(SPRN_TLB0CFG);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
tlbcfg = mfspr(SPRN_TLB1CFG);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
tlbcfg = mfspr(SPRN_TLB2CFG);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
tlbcfg = mfspr(SPRN_TLB3CFG);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("Unsupported TLB number !\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
nent = tlbcfg & 0xfff;
|
|
|
|
assoc = (tlbcfg >> 24) & 0xff;
|
|
|
|
for (i = 0; i < nent; i++) {
|
|
|
|
u32 mas0 = MAS0_TLBSEL(tlb);
|
|
|
|
u32 mas1 = MAS1_TSIZE(BOOK3E_PAGESZ_4K);
|
|
|
|
u64 mas2 = 0;
|
|
|
|
u64 mas7_mas3;
|
|
|
|
int esel = i, cc = i;
|
|
|
|
|
|
|
|
if (assoc != 0) {
|
|
|
|
cc = i / assoc;
|
|
|
|
esel = i % assoc;
|
|
|
|
mas2 = cc * 0x1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
mas0 |= MAS0_ESEL(esel);
|
|
|
|
mtspr(SPRN_MAS0, mas0);
|
|
|
|
mtspr(SPRN_MAS1, mas1);
|
|
|
|
mtspr(SPRN_MAS2, mas2);
|
|
|
|
asm volatile("tlbre 0,0,0" : : : "memory");
|
|
|
|
mas1 = mfspr(SPRN_MAS1);
|
|
|
|
mas2 = mfspr(SPRN_MAS2);
|
|
|
|
mas7_mas3 = mfspr(SPRN_MAS7_MAS3);
|
|
|
|
if (assoc && (i % assoc) == 0)
|
|
|
|
new_cc = 1;
|
|
|
|
if (!(mas1 & MAS1_VALID))
|
|
|
|
continue;
|
|
|
|
if (assoc == 0)
|
|
|
|
printf("%04x- ", i);
|
|
|
|
else if (new_cc)
|
|
|
|
printf("%04x-%c", cc, 'A' + esel);
|
|
|
|
else
|
|
|
|
printf(" |%c", 'A' + esel);
|
|
|
|
new_cc = 0;
|
|
|
|
printf(" %016llx %04x %s %c%c AS%c",
|
|
|
|
mas2 & ~0x3ffull,
|
|
|
|
(mas1 >> 16) & 0x3fff,
|
|
|
|
pgsz_names[(mas1 >> 7) & 0x1f],
|
|
|
|
mas1 & MAS1_IND ? 'I' : ' ',
|
|
|
|
mas1 & MAS1_IPROT ? 'P' : ' ',
|
|
|
|
mas1 & MAS1_TS ? '1' : '0');
|
|
|
|
printf(" %c%c%c%c%c%c%c",
|
|
|
|
mas2 & MAS2_X0 ? 'a' : ' ',
|
|
|
|
mas2 & MAS2_X1 ? 'v' : ' ',
|
|
|
|
mas2 & MAS2_W ? 'w' : ' ',
|
|
|
|
mas2 & MAS2_I ? 'i' : ' ',
|
|
|
|
mas2 & MAS2_M ? 'm' : ' ',
|
|
|
|
mas2 & MAS2_G ? 'g' : ' ',
|
|
|
|
mas2 & MAS2_E ? 'e' : ' ');
|
|
|
|
printf(" %016llx", mas7_mas3 & ramask & ~0x7ffull);
|
|
|
|
if (mas1 & MAS1_IND)
|
|
|
|
printf(" %s\n",
|
|
|
|
pgsz_names[(mas7_mas3 >> 1) & 0x1f]);
|
|
|
|
else
|
|
|
|
printf(" U%c%c%c S%c%c%c\n",
|
|
|
|
mas7_mas3 & MAS3_UX ? 'x' : ' ',
|
|
|
|
mas7_mas3 & MAS3_UW ? 'w' : ' ',
|
|
|
|
mas7_mas3 & MAS3_UR ? 'r' : ' ',
|
|
|
|
mas7_mas3 & MAS3_SX ? 'x' : ' ',
|
|
|
|
mas7_mas3 & MAS3_SW ? 'w' : ' ',
|
|
|
|
mas7_mas3 & MAS3_SR ? 'r' : ' ');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PPC_BOOK3E */
|
|
|
|
|
2008-05-08 12:27:16 +08:00
|
|
|
static void xmon_init(int enable)
|
2005-08-05 01:26:42 +08:00
|
|
|
{
|
|
|
|
if (enable) {
|
|
|
|
__debugger = xmon;
|
|
|
|
__debugger_ipi = xmon_ipi;
|
|
|
|
__debugger_bpt = xmon_bpt;
|
|
|
|
__debugger_sstep = xmon_sstep;
|
|
|
|
__debugger_iabr_match = xmon_iabr_match;
|
2012-12-20 22:06:44 +08:00
|
|
|
__debugger_break_match = xmon_break_match;
|
2005-08-05 01:26:42 +08:00
|
|
|
__debugger_fault_handler = xmon_fault_handler;
|
|
|
|
} else {
|
|
|
|
__debugger = NULL;
|
|
|
|
__debugger_ipi = NULL;
|
|
|
|
__debugger_bpt = NULL;
|
|
|
|
__debugger_sstep = NULL;
|
|
|
|
__debugger_iabr_match = NULL;
|
2012-12-20 22:06:44 +08:00
|
|
|
__debugger_break_match = NULL;
|
2005-08-05 01:26:42 +08:00
|
|
|
__debugger_fault_handler = NULL;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-11-08 19:55:08 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_MAGIC_SYSRQ
|
2010-08-18 12:15:46 +08:00
|
|
|
static void sysrq_handle_xmon(int key)
|
2005-11-08 19:55:08 +08:00
|
|
|
{
|
|
|
|
/* ensure xmon is enabled */
|
|
|
|
xmon_init(1);
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
debugger(get_irq_regs());
|
2005-11-08 19:55:08 +08:00
|
|
|
}
|
|
|
|
|
2010-08-18 12:15:46 +08:00
|
|
|
static struct sysrq_key_op sysrq_xmon_op = {
|
2005-11-08 19:55:08 +08:00
|
|
|
.handler = sysrq_handle_xmon,
|
2013-05-01 06:28:54 +08:00
|
|
|
.help_msg = "xmon(x)",
|
2005-11-08 19:55:08 +08:00
|
|
|
.action_msg = "Entering xmon",
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init setup_xmon_sysrq(void)
|
|
|
|
{
|
|
|
|
register_sysrq_key('x', &sysrq_xmon_op);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
__initcall(setup_xmon_sysrq);
|
|
|
|
#endif /* CONFIG_MAGIC_SYSRQ */
|
2006-10-03 12:12:08 +08:00
|
|
|
|
2007-06-24 14:57:08 +08:00
|
|
|
static int __initdata xmon_early, xmon_off;
|
2006-10-03 12:12:08 +08:00
|
|
|
|
|
|
|
static int __init early_parse_xmon(char *p)
|
|
|
|
{
|
|
|
|
if (!p || strncmp(p, "early", 5) == 0) {
|
|
|
|
/* just "xmon" is equivalent to "xmon=early" */
|
|
|
|
xmon_init(1);
|
|
|
|
xmon_early = 1;
|
|
|
|
} else if (strncmp(p, "on", 2) == 0)
|
|
|
|
xmon_init(1);
|
|
|
|
else if (strncmp(p, "off", 3) == 0)
|
|
|
|
xmon_off = 1;
|
|
|
|
else if (strncmp(p, "nobt", 4) == 0)
|
|
|
|
xmon_no_auto_backtrace = 1;
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
early_param("xmon", early_parse_xmon);
|
|
|
|
|
|
|
|
void __init xmon_setup(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_XMON_DEFAULT
|
|
|
|
if (!xmon_off)
|
|
|
|
xmon_init(1);
|
|
|
|
#endif
|
|
|
|
if (xmon_early)
|
|
|
|
debugger(NULL);
|
|
|
|
}
|
2006-10-25 00:31:27 +08:00
|
|
|
|
2006-11-28 02:18:55 +08:00
|
|
|
#ifdef CONFIG_SPU_BASE
|
2006-10-25 00:31:27 +08:00
|
|
|
|
|
|
|
struct spu_info {
|
|
|
|
struct spu *spu;
|
|
|
|
u64 saved_mfc_sr1_RW;
|
|
|
|
u32 saved_spu_runcntl_RW;
|
2006-11-23 07:46:41 +08:00
|
|
|
unsigned long dump_addr;
|
2006-10-25 00:31:27 +08:00
|
|
|
u8 stopped_ok;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define XMON_NUM_SPUS 16 /* Enough for current hardware */
|
|
|
|
|
|
|
|
static struct spu_info spu_info[XMON_NUM_SPUS];
|
|
|
|
|
|
|
|
void xmon_register_spus(struct list_head *list)
|
|
|
|
{
|
|
|
|
struct spu *spu;
|
|
|
|
|
|
|
|
list_for_each_entry(spu, list, full_list) {
|
|
|
|
if (spu->number >= XMON_NUM_SPUS) {
|
|
|
|
WARN_ON(1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
spu_info[spu->number].spu = spu;
|
|
|
|
spu_info[spu->number].stopped_ok = 0;
|
2006-11-23 07:46:41 +08:00
|
|
|
spu_info[spu->number].dump_addr = (unsigned long)
|
|
|
|
spu_info[spu->number].spu->local_store;
|
2006-10-25 00:31:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stop_spus(void)
|
|
|
|
{
|
|
|
|
struct spu *spu;
|
|
|
|
int i;
|
|
|
|
u64 tmp;
|
|
|
|
|
|
|
|
for (i = 0; i < XMON_NUM_SPUS; i++) {
|
|
|
|
if (!spu_info[i].spu)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
|
|
|
|
spu = spu_info[i].spu;
|
|
|
|
|
|
|
|
spu_info[i].saved_spu_runcntl_RW =
|
|
|
|
in_be32(&spu->problem->spu_runcntl_RW);
|
|
|
|
|
|
|
|
tmp = spu_mfc_sr1_get(spu);
|
|
|
|
spu_info[i].saved_mfc_sr1_RW = tmp;
|
|
|
|
|
|
|
|
tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
|
|
|
|
spu_mfc_sr1_set(spu, tmp);
|
|
|
|
|
|
|
|
sync();
|
|
|
|
__delay(200);
|
|
|
|
|
|
|
|
spu_info[i].stopped_ok = 1;
|
2006-11-23 07:46:40 +08:00
|
|
|
|
|
|
|
printf("Stopped spu %.2d (was %s)\n", i,
|
|
|
|
spu_info[i].saved_spu_runcntl_RW ?
|
|
|
|
"running" : "stopped");
|
2006-10-25 00:31:27 +08:00
|
|
|
} else {
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
printf("*** Error stopping spu %.2d\n", i);
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void restart_spus(void)
|
|
|
|
{
|
|
|
|
struct spu *spu;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < XMON_NUM_SPUS; i++) {
|
|
|
|
if (!spu_info[i].spu)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!spu_info[i].stopped_ok) {
|
|
|
|
printf("*** Error, spu %d was not successfully stopped"
|
|
|
|
", not restarting\n", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
|
|
|
|
spu = spu_info[i].spu;
|
|
|
|
spu_mfc_sr1_set(spu, spu_info[i].saved_mfc_sr1_RW);
|
|
|
|
out_be32(&spu->problem->spu_runcntl_RW,
|
|
|
|
spu_info[i].saved_spu_runcntl_RW);
|
|
|
|
|
|
|
|
sync();
|
|
|
|
__delay(200);
|
|
|
|
|
|
|
|
printf("Restarted spu %.2d\n", i);
|
|
|
|
} else {
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
printf("*** Error restarting spu %.2d\n", i);
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-25 00:31:28 +08:00
|
|
|
#define DUMP_WIDTH 23
|
2006-11-23 07:46:39 +08:00
|
|
|
#define DUMP_VALUE(format, field, value) \
|
2006-10-25 00:31:28 +08:00
|
|
|
do { \
|
|
|
|
if (setjmp(bus_error_jmp) == 0) { \
|
|
|
|
catch_memory_errors = 1; \
|
|
|
|
sync(); \
|
|
|
|
printf(" %-*s = "format"\n", DUMP_WIDTH, \
|
2006-11-23 07:46:39 +08:00
|
|
|
#field, value); \
|
2006-10-25 00:31:28 +08:00
|
|
|
sync(); \
|
|
|
|
__delay(200); \
|
|
|
|
} else { \
|
|
|
|
catch_memory_errors = 0; \
|
|
|
|
printf(" %-*s = *** Error reading field.\n", \
|
|
|
|
DUMP_WIDTH, #field); \
|
|
|
|
} \
|
|
|
|
catch_memory_errors = 0; \
|
|
|
|
} while (0)
|
|
|
|
|
2006-11-23 07:46:39 +08:00
|
|
|
#define DUMP_FIELD(obj, format, field) \
|
|
|
|
DUMP_VALUE(format, field, obj->field)
|
|
|
|
|
2006-10-25 00:31:28 +08:00
|
|
|
static void dump_spu_fields(struct spu *spu)
|
|
|
|
{
|
|
|
|
printf("Dumping spu fields at address %p:\n", spu);
|
|
|
|
|
|
|
|
DUMP_FIELD(spu, "0x%x", number);
|
|
|
|
DUMP_FIELD(spu, "%s", name);
|
|
|
|
DUMP_FIELD(spu, "0x%lx", local_store_phys);
|
|
|
|
DUMP_FIELD(spu, "0x%p", local_store);
|
|
|
|
DUMP_FIELD(spu, "0x%lx", ls_size);
|
|
|
|
DUMP_FIELD(spu, "0x%x", node);
|
|
|
|
DUMP_FIELD(spu, "0x%lx", flags);
|
|
|
|
DUMP_FIELD(spu, "%d", class_0_pending);
|
2008-04-28 02:41:55 +08:00
|
|
|
DUMP_FIELD(spu, "0x%lx", class_0_dar);
|
|
|
|
DUMP_FIELD(spu, "0x%lx", class_1_dar);
|
|
|
|
DUMP_FIELD(spu, "0x%lx", class_1_dsisr);
|
2006-10-25 00:31:28 +08:00
|
|
|
DUMP_FIELD(spu, "0x%lx", irqs[0]);
|
|
|
|
DUMP_FIELD(spu, "0x%lx", irqs[1]);
|
|
|
|
DUMP_FIELD(spu, "0x%lx", irqs[2]);
|
|
|
|
DUMP_FIELD(spu, "0x%x", slb_replace);
|
|
|
|
DUMP_FIELD(spu, "%d", pid);
|
|
|
|
DUMP_FIELD(spu, "0x%p", mm);
|
|
|
|
DUMP_FIELD(spu, "0x%p", ctx);
|
|
|
|
DUMP_FIELD(spu, "0x%p", rq);
|
|
|
|
DUMP_FIELD(spu, "0x%p", timestamp);
|
|
|
|
DUMP_FIELD(spu, "0x%lx", problem_phys);
|
|
|
|
DUMP_FIELD(spu, "0x%p", problem);
|
2006-11-23 07:46:39 +08:00
|
|
|
DUMP_VALUE("0x%x", problem->spu_runcntl_RW,
|
|
|
|
in_be32(&spu->problem->spu_runcntl_RW));
|
|
|
|
DUMP_VALUE("0x%x", problem->spu_status_R,
|
|
|
|
in_be32(&spu->problem->spu_status_R));
|
|
|
|
DUMP_VALUE("0x%x", problem->spu_npc_RW,
|
|
|
|
in_be32(&spu->problem->spu_npc_RW));
|
2006-10-25 00:31:28 +08:00
|
|
|
DUMP_FIELD(spu, "0x%p", priv2);
|
2006-11-23 07:46:50 +08:00
|
|
|
DUMP_FIELD(spu, "0x%p", pdata);
|
2006-10-25 00:31:28 +08:00
|
|
|
}
|
|
|
|
|
2006-11-23 07:46:44 +08:00
|
|
|
int
|
|
|
|
spu_inst_dump(unsigned long adr, long count, int praddr)
|
|
|
|
{
|
|
|
|
return generic_inst_dump(adr, count, praddr, print_insn_spu);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_spu_ls(unsigned long num, int subcmd)
|
2006-11-23 07:46:41 +08:00
|
|
|
{
|
|
|
|
unsigned long offset, addr, ls_addr;
|
|
|
|
|
|
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
|
|
catch_memory_errors = 1;
|
|
|
|
sync();
|
|
|
|
ls_addr = (unsigned long)spu_info[num].spu->local_store;
|
|
|
|
sync();
|
|
|
|
__delay(200);
|
|
|
|
} else {
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
printf("*** Error: accessing spu info for spu %d\n", num);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
catch_memory_errors = 0;
|
|
|
|
|
|
|
|
if (scanhex(&offset))
|
|
|
|
addr = ls_addr + offset;
|
|
|
|
else
|
|
|
|
addr = spu_info[num].dump_addr;
|
|
|
|
|
|
|
|
if (addr >= ls_addr + LS_SIZE) {
|
|
|
|
printf("*** Error: address outside of local store\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-11-23 07:46:44 +08:00
|
|
|
switch (subcmd) {
|
|
|
|
case 'i':
|
|
|
|
addr += spu_inst_dump(addr, 16, 1);
|
|
|
|
last_cmd = "sdi\n";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
prdump(addr, 64);
|
|
|
|
addr += 64;
|
|
|
|
last_cmd = "sd\n";
|
|
|
|
break;
|
|
|
|
}
|
2006-11-23 07:46:41 +08:00
|
|
|
|
|
|
|
spu_info[num].dump_addr = addr;
|
|
|
|
}
|
|
|
|
|
2006-10-25 00:31:27 +08:00
|
|
|
static int do_spu_cmd(void)
|
|
|
|
{
|
2006-11-23 07:46:41 +08:00
|
|
|
static unsigned long num = 0;
|
2006-11-23 07:46:44 +08:00
|
|
|
int cmd, subcmd = 0;
|
2006-10-25 00:31:27 +08:00
|
|
|
|
|
|
|
cmd = inchar();
|
|
|
|
switch (cmd) {
|
|
|
|
case 's':
|
|
|
|
stop_spus();
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
restart_spus();
|
|
|
|
break;
|
2006-11-23 07:46:41 +08:00
|
|
|
case 'd':
|
2006-11-23 07:46:44 +08:00
|
|
|
subcmd = inchar();
|
|
|
|
if (isxdigit(subcmd) || subcmd == '\n')
|
|
|
|
termch = subcmd;
|
|
|
|
case 'f':
|
2006-11-23 07:46:41 +08:00
|
|
|
scanhex(&num);
|
|
|
|
if (num >= XMON_NUM_SPUS || !spu_info[num].spu) {
|
2006-10-25 00:31:28 +08:00
|
|
|
printf("*** Error: invalid spu number\n");
|
2006-11-23 07:46:41 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case 'f':
|
|
|
|
dump_spu_fields(spu_info[num].spu);
|
|
|
|
break;
|
|
|
|
default:
|
2006-11-23 07:46:44 +08:00
|
|
|
dump_spu_ls(num, subcmd);
|
2006-11-23 07:46:41 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-10-25 00:31:28 +08:00
|
|
|
break;
|
2006-10-25 00:31:27 +08:00
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2006-11-28 02:18:55 +08:00
|
|
|
#else /* ! CONFIG_SPU_BASE */
|
2006-10-25 00:31:27 +08:00
|
|
|
static int do_spu_cmd(void)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|