6879dc137e
This patch kills the whole embedded System.map mecanism and the bootloader-passed System.map that was used to provide symbol resolution in xmon. Instead, xmon now uses kallsyms like ppc64 does. No hurry getting that in Linus tree, let it be tested in -mm for a while first and make sure it doesn't break various embedded configs. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2513 lines
53 KiB
C
2513 lines
53 KiB
C
/*
|
|
* Routines providing a simple monitor for use on the PowerMac.
|
|
*
|
|
* Copyright (C) 1996 Paul Mackerras.
|
|
*
|
|
* 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/config.h>
|
|
#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>
|
|
#include <linux/cpumask.h>
|
|
|
|
#include <asm/ptrace.h>
|
|
#include <asm/string.h>
|
|
#include <asm/prom.h>
|
|
#include <asm/machdep.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/mmu.h>
|
|
#include <asm/mmu_context.h>
|
|
#include <asm/paca.h>
|
|
#include <asm/ppcdebug.h>
|
|
#include <asm/cputable.h>
|
|
#include <asm/rtas.h>
|
|
#include <asm/sstep.h>
|
|
#include <asm/bug.h>
|
|
#include <asm/hvcall.h>
|
|
|
|
#include "nonstdio.h"
|
|
#include "privinst.h"
|
|
|
|
#define scanhex xmon_scanhex
|
|
#define skipbl xmon_skipbl
|
|
|
|
#ifdef CONFIG_SMP
|
|
cpumask_t cpus_in_xmon = CPU_MASK_NONE;
|
|
static unsigned long xmon_taken = 1;
|
|
static int xmon_owner;
|
|
static int xmon_gate;
|
|
#endif /* CONFIG_SMP */
|
|
|
|
static unsigned long in_xmon = 0;
|
|
|
|
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];
|
|
|
|
#define JMP_BUF_LEN (184/sizeof(long))
|
|
static long bus_error_jmp[JMP_BUF_LEN];
|
|
static int catch_memory_errors;
|
|
static long *xmon_fault_jmp[NR_CPUS];
|
|
#define setjmp xmon_setjmp
|
|
#define longjmp xmon_longjmp
|
|
|
|
/* Breakpoint stuff */
|
|
struct bpt {
|
|
unsigned long address;
|
|
unsigned int instr[2];
|
|
atomic_t ref_count;
|
|
int enabled;
|
|
unsigned long pad;
|
|
};
|
|
|
|
/* Bits in bpt.enabled */
|
|
#define BP_IABR_TE 1 /* IABR translation enabled */
|
|
#define BP_IABR 2
|
|
#define BP_TRAP 8
|
|
#define BP_DABR 0x10
|
|
|
|
#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);
|
|
void print_address(unsigned long);
|
|
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);
|
|
void dump_segments(void);
|
|
static void symbol_lookup(void);
|
|
static void xmon_print_symbol(unsigned long address, const char *mid,
|
|
const char *after);
|
|
static const char *getvecname(unsigned long vec);
|
|
|
|
static void debug_trace(void);
|
|
|
|
extern int print_insn_powerpc(unsigned long, unsigned long, int);
|
|
extern void printf(const char *fmt, ...);
|
|
extern void xmon_vfprintf(void *f, const char *fmt, va_list ap);
|
|
extern int xmon_putc(int c, void *f);
|
|
extern int putchar(int ch);
|
|
extern int xmon_read_poll(void);
|
|
extern int setjmp(long *);
|
|
extern void longjmp(long *, int);
|
|
extern unsigned long _ASR;
|
|
|
|
#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])
|
|
|
|
#define isxdigit(c) (('0' <= (c) && (c) <= '9') \
|
|
|| ('a' <= (c) && (c) <= 'f') \
|
|
|| ('A' <= (c) && (c) <= 'F'))
|
|
#define isalnum(c) (('0' <= (c) && (c) <= '9') \
|
|
|| ('a' <= (c) && (c) <= 'z') \
|
|
|| ('A' <= (c) && (c) <= 'Z'))
|
|
#define isspace(c) (c == ' ' || c == '\t' || c == 10 || c == 13 || c == 0)
|
|
|
|
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\
|
|
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\
|
|
p show the task list\n\
|
|
r print registers\n\
|
|
s single step\n\
|
|
S print special registers\n\
|
|
t print backtrace\n\
|
|
T Enable/Disable PPCDBG flags\n\
|
|
x exit monitor and recover\n\
|
|
X exit monitor and dont recover\n\
|
|
u dump segment table or SLB\n\
|
|
? help\n"
|
|
"\
|
|
zr reboot\n\
|
|
zh halt\n"
|
|
;
|
|
|
|
static struct pt_regs *xmon_regs;
|
|
|
|
extern inline void sync(void)
|
|
{
|
|
asm volatile("sync; isync");
|
|
}
|
|
|
|
/* (Ref: 64-bit PowerPC ELF ABI Spplement; Ian Lance Taylor, Zembu Labs).
|
|
A PPC stack frame looks like this:
|
|
|
|
High Address
|
|
Back Chain
|
|
FP reg save area
|
|
GP reg save area
|
|
Local var space
|
|
Parameter save area (SP+48)
|
|
TOC save area (SP+40)
|
|
link editor doubleword (SP+32)
|
|
compiler doubleword (SP+24)
|
|
LR save (SP+16)
|
|
CR save (SP+8)
|
|
Back Chain (SP+0)
|
|
|
|
Note that the LR (ret addr) may not be saved in the current frame if
|
|
no functions have been called from the current function.
|
|
*/
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
args.token = rtas_token("set-indicator");
|
|
if (args.token == RTAS_UNKNOWN_SERVICE)
|
|
return;
|
|
args.nargs = 3;
|
|
args.nret = 1;
|
|
args.rets = &args.args[3];
|
|
args.args[0] = SURVEILLANCE_TOKEN;
|
|
args.args[1] = 0;
|
|
args.args[2] = 0;
|
|
enter_rtas(__pa(&args));
|
|
#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;
|
|
for (;;) {
|
|
if (xmon_speaker == 0) {
|
|
last_speaker = cmpxchg(&xmon_speaker, 0, me);
|
|
if (last_speaker == 0)
|
|
return;
|
|
}
|
|
timeout = 10000000;
|
|
while (xmon_speaker == last_speaker) {
|
|
if (--timeout > 0)
|
|
continue;
|
|
/* hostile takeover */
|
|
prev = cmpxchg(&xmon_speaker, last_speaker, me);
|
|
if (prev == last_speaker)
|
|
return;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void release_output_lock(void)
|
|
{
|
|
xmon_speaker = 0;
|
|
}
|
|
#endif
|
|
|
|
int xmon_core(struct pt_regs *regs, int fromipi)
|
|
{
|
|
int cmd = 0;
|
|
unsigned long msr;
|
|
struct bpt *bp;
|
|
long recurse_jmp[JMP_BUF_LEN];
|
|
unsigned long offset;
|
|
#ifdef CONFIG_SMP
|
|
int cpu;
|
|
int secondary;
|
|
unsigned long timeout;
|
|
#endif
|
|
|
|
msr = get_msr();
|
|
set_msrd(msr & ~MSR_EE); /* disable interrupts */
|
|
|
|
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();
|
|
if (cpu_isset(cpu, cpus_in_xmon)) {
|
|
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)));
|
|
longjmp(xmon_fault_jmp[cpu], 1);
|
|
}
|
|
|
|
if (setjmp(recurse_jmp) != 0) {
|
|
if (!in_xmon || !xmon_gate) {
|
|
printf("xmon: WARNING: bad recursive fault "
|
|
"on cpu 0x%x\n", cpu);
|
|
goto waiting;
|
|
}
|
|
secondary = !(xmon_taken && cpu == xmon_owner);
|
|
goto cmdloop;
|
|
}
|
|
|
|
xmon_fault_jmp[cpu] = recurse_jmp;
|
|
cpu_set(cpu, cpus_in_xmon);
|
|
|
|
bp = NULL;
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF))
|
|
bp = at_breakpoint(regs->nip);
|
|
if (bp || (regs->msr & MSR_RI) == 0)
|
|
fromipi = 0;
|
|
|
|
if (!fromipi) {
|
|
get_output_lock();
|
|
excprint(regs);
|
|
if (bp) {
|
|
printf("cpu 0x%x stopped at breakpoint 0x%x (",
|
|
cpu, BP_NUM(bp));
|
|
xmon_print_symbol(regs->nip, " ", ")\n");
|
|
}
|
|
if ((regs->msr & MSR_RI) == 0)
|
|
printf("WARNING: exception is not recoverable, "
|
|
"can't continue\n");
|
|
release_output_lock();
|
|
}
|
|
|
|
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) {
|
|
smp_send_debugger_break(MSG_ALL_BUT_SELF);
|
|
/* wait for other cpus to come in */
|
|
for (timeout = 100000000; timeout != 0; --timeout) {
|
|
if (cpus_weight(cpus_in_xmon) >= ncpus)
|
|
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:
|
|
cpu_clear(cpu, cpus_in_xmon);
|
|
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) {
|
|
printf("Stopped at breakpoint %x (", BP_NUM(bp));
|
|
xmon_print_symbol(regs->nip, " ", ")\n");
|
|
}
|
|
if ((regs->msr & MSR_RI) == 0)
|
|
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
|
|
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
|
|
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"));
|
|
}
|
|
}
|
|
}
|
|
|
|
insert_cpu_bpts();
|
|
|
|
set_msrd(msr); /* restore interrupt enable */
|
|
|
|
return cmd != 'X';
|
|
}
|
|
|
|
int xmon(struct pt_regs *excp)
|
|
{
|
|
struct pt_regs regs;
|
|
|
|
if (excp == NULL) {
|
|
/* Ok, grab regs as they are now.
|
|
This won't do a particularily good job because the
|
|
prologue has already been executed.
|
|
ToDo: We could reach back into the callers save
|
|
area to do a better job of representing the
|
|
caller's state.
|
|
*/
|
|
asm volatile ("std 0,0(%0)\n\
|
|
std 1,8(%0)\n\
|
|
std 2,16(%0)\n\
|
|
std 3,24(%0)\n\
|
|
std 4,32(%0)\n\
|
|
std 5,40(%0)\n\
|
|
std 6,48(%0)\n\
|
|
std 7,56(%0)\n\
|
|
std 8,64(%0)\n\
|
|
std 9,72(%0)\n\
|
|
std 10,80(%0)\n\
|
|
std 11,88(%0)\n\
|
|
std 12,96(%0)\n\
|
|
std 13,104(%0)\n\
|
|
std 14,112(%0)\n\
|
|
std 15,120(%0)\n\
|
|
std 16,128(%0)\n\
|
|
std 17,136(%0)\n\
|
|
std 18,144(%0)\n\
|
|
std 19,152(%0)\n\
|
|
std 20,160(%0)\n\
|
|
std 21,168(%0)\n\
|
|
std 22,176(%0)\n\
|
|
std 23,184(%0)\n\
|
|
std 24,192(%0)\n\
|
|
std 25,200(%0)\n\
|
|
std 26,208(%0)\n\
|
|
std 27,216(%0)\n\
|
|
std 28,224(%0)\n\
|
|
std 29,232(%0)\n\
|
|
std 30,240(%0)\n\
|
|
std 31,248(%0)" : : "b" (®s));
|
|
|
|
regs.nip = regs.link = ((unsigned long *)(regs.gpr[1]))[2];
|
|
regs.msr = get_msr();
|
|
regs.ctr = get_ctr();
|
|
regs.xer = get_xer();
|
|
regs.ccr = get_cr();
|
|
regs.trap = 0;
|
|
excp = ®s;
|
|
}
|
|
return xmon_core(excp, 0);
|
|
}
|
|
|
|
int xmon_bpt(struct pt_regs *regs)
|
|
{
|
|
struct bpt *bp;
|
|
unsigned long offset;
|
|
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
|
|
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;
|
|
}
|
|
|
|
int xmon_sstep(struct pt_regs *regs)
|
|
{
|
|
if (user_mode(regs))
|
|
return 0;
|
|
xmon_core(regs, 0);
|
|
return 1;
|
|
}
|
|
|
|
int xmon_dabr_match(struct pt_regs *regs)
|
|
{
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
|
|
return 0;
|
|
xmon_core(regs, 0);
|
|
return 1;
|
|
}
|
|
|
|
int xmon_iabr_match(struct pt_regs *regs)
|
|
{
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) != (MSR_IR|MSR_SF))
|
|
return 0;
|
|
if (iabr == 0)
|
|
return 0;
|
|
xmon_core(regs, 0);
|
|
return 1;
|
|
}
|
|
|
|
int xmon_ipi(struct pt_regs *regs)
|
|
{
|
|
#ifdef CONFIG_SMP
|
|
if (in_xmon && !cpu_isset(smp_processor_id(), cpus_in_xmon))
|
|
xmon_core(regs, 1);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int xmon_fault_handler(struct pt_regs *regs)
|
|
{
|
|
struct bpt *bp;
|
|
unsigned long offset;
|
|
|
|
if (in_xmon && catch_memory_errors)
|
|
handle_fault(regs); /* doesn't return */
|
|
|
|
if ((regs->msr & (MSR_IR|MSR_PR|MSR_SF)) == (MSR_IR|MSR_SF)) {
|
|
bp = in_breakpoint_table(regs->nip, &offset);
|
|
if (bp != NULL) {
|
|
regs->nip = bp->address + offset;
|
|
atomic_dec(&bp->ref_count);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* On systems with a hypervisor, we can't set the DABR
|
|
(data address breakpoint register) directly. */
|
|
static void set_controlled_dabr(unsigned long val)
|
|
{
|
|
#ifdef CONFIG_PPC_PSERIES
|
|
if (systemcfg->platform == PLATFORM_PSERIES_LPAR) {
|
|
int rc = plpar_hcall_norets(H_SET_DABR, val);
|
|
if (rc != H_Success)
|
|
xmon_printf("Warning: setting DABR failed (%d)\n", rc);
|
|
} else
|
|
#endif
|
|
set_dabr(val);
|
|
}
|
|
|
|
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) {
|
|
if ((bp->enabled & (BP_TRAP|BP_IABR)) == 0)
|
|
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]);
|
|
if (bp->enabled & BP_IABR)
|
|
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)
|
|
{
|
|
if (dabr.enabled)
|
|
set_controlled_dabr(dabr.address | (dabr.enabled & 7));
|
|
if (iabr && cpu_has_feature(CPU_FTR_IABR))
|
|
set_iabr(iabr->address
|
|
| (iabr->enabled & (BP_IABR|BP_IABR_TE)));
|
|
}
|
|
|
|
static void remove_bpts(void)
|
|
{
|
|
int i;
|
|
struct bpt *bp;
|
|
unsigned instr;
|
|
|
|
bp = bpts;
|
|
for (i = 0; i < NBPTS; ++i, ++bp) {
|
|
if ((bp->enabled & (BP_TRAP|BP_IABR)) != BP_TRAP)
|
|
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)
|
|
{
|
|
set_controlled_dabr(0);
|
|
if (cpu_has_feature(CPU_FTR_IABR))
|
|
set_iabr(0);
|
|
}
|
|
|
|
/* Command interpreting routine */
|
|
static char *last_cmd;
|
|
|
|
static int
|
|
cmds(struct pt_regs *excp)
|
|
{
|
|
int cmd = 0;
|
|
|
|
last_cmd = NULL;
|
|
xmon_regs = excp;
|
|
for(;;) {
|
|
#ifdef CONFIG_SMP
|
|
printf("%x:", smp_processor_id());
|
|
#endif /* CONFIG_SMP */
|
|
printf("mon> ");
|
|
fflush(stdout);
|
|
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':
|
|
show_mem();
|
|
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':
|
|
if (do_step(excp))
|
|
return cmd;
|
|
break;
|
|
case 'x':
|
|
case 'X':
|
|
case EOF:
|
|
return cmd;
|
|
case '?':
|
|
printf(help_string);
|
|
break;
|
|
case 'p':
|
|
show_state();
|
|
break;
|
|
case 'b':
|
|
bpt_cmds();
|
|
break;
|
|
case 'C':
|
|
csum();
|
|
break;
|
|
case 'c':
|
|
if (cpu_cmd())
|
|
return 0;
|
|
break;
|
|
case 'z':
|
|
bootcmds();
|
|
break;
|
|
case 'T':
|
|
debug_trace();
|
|
break;
|
|
case 'u':
|
|
dump_segments();
|
|
break;
|
|
default:
|
|
printf("Unrecognized command: ");
|
|
do {
|
|
if (' ' < cmd && cmd <= '~')
|
|
putchar(cmd);
|
|
else
|
|
printf("\\x%x", cmd);
|
|
cmd = inchar();
|
|
} while (cmd != '\n');
|
|
printf(" (type ? for help)\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 */
|
|
if ((regs->msr & (MSR_SF|MSR_PR|MSR_IR)) == (MSR_SF|MSR_IR)) {
|
|
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;
|
|
}
|
|
|
|
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')
|
|
ppc_md.power_off();
|
|
}
|
|
|
|
static int cpu_cmd(void)
|
|
{
|
|
#ifdef CONFIG_SMP
|
|
unsigned long cpu;
|
|
int timeout;
|
|
int count;
|
|
|
|
if (!scanhex(&cpu)) {
|
|
/* print cpus waiting or in xmon */
|
|
printf("cpus stopped:");
|
|
count = 0;
|
|
for (cpu = 0; cpu < NR_CPUS; ++cpu) {
|
|
if (cpu_isset(cpu, cpus_in_xmon)) {
|
|
if (count == 0)
|
|
printf(" %x", cpu);
|
|
++count;
|
|
} else {
|
|
if (count > 1)
|
|
printf("-%x", cpu - 1);
|
|
count = 0;
|
|
}
|
|
}
|
|
if (count > 1)
|
|
printf("-%x", NR_CPUS - 1);
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
/* try to switch to cpu specified */
|
|
if (!cpu_isset(cpu, cpus_in_xmon)) {
|
|
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();
|
|
printf("cpu %u didn't take control\n", cpu);
|
|
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) {
|
|
printf("csum stopped at %x\n", adrs+i);
|
|
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;
|
|
if (addr < KERNELBASE) {
|
|
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;
|
|
}
|
|
|
|
static char *breakpoint_help_string =
|
|
"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"
|
|
"bi <addr> [cnt] set hardware instr breakpoint (POWER3/RS64 only)\n"
|
|
"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) {
|
|
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)) {
|
|
if (dabr.address < KERNELBASE) {
|
|
printf(badaddr);
|
|
break;
|
|
}
|
|
dabr.address &= ~7;
|
|
dabr.enabled = mode | BP_DABR;
|
|
}
|
|
break;
|
|
|
|
case 'i': /* bi - hardware instr breakpoint */
|
|
if (!cpu_has_feature(CPU_FTR_IABR)) {
|
|
printf("Hardware instruction breakpoint "
|
|
"not supported on this cpu\n");
|
|
break;
|
|
}
|
|
if (iabr) {
|
|
iabr->enabled &= ~(BP_IABR | BP_IABR_TE);
|
|
iabr = NULL;
|
|
}
|
|
if (!scanhex(&a))
|
|
break;
|
|
if (!check_bp_loc(a))
|
|
break;
|
|
bp = new_breakpoint(a);
|
|
if (bp != NULL) {
|
|
bp->enabled |= BP_IABR | BP_IABR_TE;
|
|
iabr = bp;
|
|
}
|
|
break;
|
|
|
|
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);
|
|
if (bp == 0) {
|
|
printf("No breakpoint at %x\n", a);
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("Cleared breakpoint %x (", BP_NUM(bp));
|
|
xmon_print_symbol(bp->address, " ", ")\n");
|
|
bp->enabled = 0;
|
|
break;
|
|
|
|
default:
|
|
termch = cmd;
|
|
cmd = skipbl();
|
|
if (cmd == '?') {
|
|
printf(breakpoint_help_string);
|
|
break;
|
|
}
|
|
termch = cmd;
|
|
if (!scanhex(&a)) {
|
|
/* print all breakpoints */
|
|
printf(" type address\n");
|
|
if (dabr.enabled) {
|
|
printf(" data %.16lx [", dabr.address);
|
|
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),
|
|
(bp->enabled & BP_IABR)? "inst": "trap");
|
|
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;
|
|
case 0xc00: ret = "(System Call)"; break;
|
|
case 0xd00: ret = "(Single Step)"; break;
|
|
case 0xf00: ret = "(Performance Monitor)"; break;
|
|
case 0xf20: ret = "(Altivec Unavailable)"; break;
|
|
case 0x1300: ret = "(Instruction Breakpoint)"; break;
|
|
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;
|
|
char *modname;
|
|
|
|
*startp = *endp = 0;
|
|
if (pc == 0)
|
|
return;
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
catch_memory_errors = 1;
|
|
sync();
|
|
name = kallsyms_lookup(pc, &size, &offset, &modname, tmpstr);
|
|
if (name != NULL) {
|
|
*startp = pc - offset;
|
|
*endp = pc - offset + size;
|
|
}
|
|
sync();
|
|
}
|
|
catch_memory_errors = 0;
|
|
}
|
|
|
|
static int xmon_depth_to_print = 64;
|
|
|
|
static void xmon_show_stack(unsigned long sp, unsigned long lr,
|
|
unsigned long pc)
|
|
{
|
|
unsigned long ip;
|
|
unsigned long newsp;
|
|
unsigned long marker;
|
|
int count = 0;
|
|
struct pt_regs regs;
|
|
|
|
do {
|
|
if (sp < PAGE_OFFSET) {
|
|
if (sp != 0)
|
|
printf("SP (%lx) is in userspace\n", sp);
|
|
break;
|
|
}
|
|
|
|
if (!mread(sp + 16, &ip, sizeof(unsigned long))
|
|
|| !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)
|
|
mread(newsp + 16, &nextip,
|
|
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) {
|
|
printf("[%.16lx] ", sp);
|
|
xmon_print_symbol(ip, " ", " (unreliable)\n");
|
|
}
|
|
pc = lr = 0;
|
|
|
|
} else {
|
|
printf("[%.16lx] ", sp);
|
|
xmon_print_symbol(ip, " ", "\n");
|
|
}
|
|
|
|
/* Look for "regshere" marker to see if this is
|
|
an exception frame. */
|
|
if (mread(sp + 0x60, &marker, sizeof(unsigned long))
|
|
&& marker == 0x7265677368657265ul) {
|
|
if (mread(sp + 0x70, ®s, sizeof(regs))
|
|
!= sizeof(regs)) {
|
|
printf("Couldn't read registers at %lx\n",
|
|
sp + 0x70);
|
|
break;
|
|
}
|
|
printf("--- Exception: %lx %s at ", regs.trap,
|
|
getvecname(TRAP(®s)));
|
|
pc = regs.nip;
|
|
lr = regs.link;
|
|
xmon_print_symbol(pc, " ", "\n");
|
|
}
|
|
|
|
if (newsp == 0)
|
|
break;
|
|
|
|
sp = newsp;
|
|
} while (count++ < xmon_depth_to_print);
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct bug_entry *bug;
|
|
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;
|
|
if (bug->line & BUG_WARNING_TRAP)
|
|
return;
|
|
|
|
printf("kernel BUG in %s at %s:%d!\n",
|
|
bug->function, bug->file, (unsigned int)bug->line);
|
|
}
|
|
|
|
void excprint(struct pt_regs *fp)
|
|
{
|
|
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);
|
|
|
|
if (trap == 0x300 || trap == 0x380 || trap == 0x600) {
|
|
printf(" dar: %lx\n", fp->dar);
|
|
if (trap != 0x380)
|
|
printf(" dsisr: %lx\n", fp->dsisr);
|
|
}
|
|
|
|
printf(" current = 0x%lx\n", current);
|
|
printf(" paca = 0x%lx\n", get_paca());
|
|
if (current) {
|
|
printf(" pid = %ld, comm = %s\n",
|
|
current->pid, current->comm);
|
|
}
|
|
|
|
if (trap == 0x700)
|
|
print_bug_trap(fp);
|
|
}
|
|
|
|
void prregs(struct pt_regs *fp)
|
|
{
|
|
int n;
|
|
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;
|
|
printf("*** Error reading registers from %.16lx\n",
|
|
base);
|
|
return;
|
|
}
|
|
catch_memory_errors = 0;
|
|
fp = ®s;
|
|
}
|
|
|
|
if (FULL_REGS(fp)) {
|
|
for (n = 0; n < 16; ++n)
|
|
printf("R%.2ld = %.16lx R%.2ld = %.16lx\n",
|
|
n, fp->gpr[n], n+16, fp->gpr[n+16]);
|
|
} else {
|
|
for (n = 0; n < 7; ++n)
|
|
printf("R%.2ld = %.16lx R%.2ld = %.16lx\n",
|
|
n, fp->gpr[n], n+7, fp->gpr[n+7]);
|
|
}
|
|
printf("pc = ");
|
|
xmon_print_symbol(fp->nip, " ", "\n");
|
|
printf("lr = ");
|
|
xmon_print_symbol(fp->link, " ", "\n");
|
|
printf("msr = %.16lx cr = %.8lx\n", fp->msr, fp->ccr);
|
|
printf("ctr = %.16lx xer = %.16lx trap = %8lx\n",
|
|
fp->ctr, fp->xer, fp->trap);
|
|
}
|
|
|
|
void cacheflush(void)
|
|
{
|
|
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;
|
|
}
|
|
|
|
unsigned long
|
|
read_spr(int n)
|
|
{
|
|
unsigned int instrs[2];
|
|
unsigned long (*code)(void);
|
|
unsigned long opd[3];
|
|
unsigned long ret = -1UL;
|
|
|
|
instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
|
|
instrs[1] = 0x4e800020;
|
|
opd[0] = (unsigned long)instrs;
|
|
opd[1] = 0;
|
|
opd[2] = 0;
|
|
store_inst(instrs);
|
|
store_inst(instrs+1);
|
|
code = (unsigned long (*)(void)) opd;
|
|
|
|
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;
|
|
}
|
|
|
|
void
|
|
write_spr(int n, unsigned long val)
|
|
{
|
|
unsigned int instrs[2];
|
|
unsigned long (*code)(unsigned long);
|
|
unsigned long opd[3];
|
|
|
|
instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
|
|
instrs[1] = 0x4e800020;
|
|
opd[0] = (unsigned long)instrs;
|
|
opd[1] = 0;
|
|
opd[2] = 0;
|
|
store_inst(instrs);
|
|
store_inst(instrs+1);
|
|
code = (unsigned long (*)(unsigned long)) opd;
|
|
|
|
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;
|
|
|
|
void
|
|
super_regs(void)
|
|
{
|
|
int cmd;
|
|
unsigned long val;
|
|
#ifdef CONFIG_PPC_ISERIES
|
|
struct paca_struct *ptrPaca = NULL;
|
|
struct lppaca *ptrLpPaca = NULL;
|
|
struct ItLpRegSave *ptrLpRegSave = NULL;
|
|
#endif
|
|
|
|
cmd = skipbl();
|
|
if (cmd == '\n') {
|
|
unsigned long sp, toc;
|
|
asm("mr %0,1" : "=r" (sp) :);
|
|
asm("mr %0,2" : "=r" (toc) :);
|
|
|
|
printf("msr = %.16lx sprg0= %.16lx\n", get_msr(), get_sprg0());
|
|
printf("pvr = %.16lx sprg1= %.16lx\n", get_pvr(), get_sprg1());
|
|
printf("dec = %.16lx sprg2= %.16lx\n", get_dec(), get_sprg2());
|
|
printf("sp = %.16lx sprg3= %.16lx\n", sp, get_sprg3());
|
|
printf("toc = %.16lx dar = %.16lx\n", toc, get_dar());
|
|
printf("srr0 = %.16lx srr1 = %.16lx\n", get_srr0(), get_srr1());
|
|
#ifdef CONFIG_PPC_ISERIES
|
|
// Dump out relevant Paca data areas.
|
|
printf("Paca: \n");
|
|
ptrPaca = get_paca();
|
|
|
|
printf(" Local Processor Control Area (LpPaca): \n");
|
|
ptrLpPaca = ptrPaca->lppaca_ptr;
|
|
printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n",
|
|
ptrLpPaca->saved_srr0, ptrLpPaca->saved_srr1);
|
|
printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n",
|
|
ptrLpPaca->saved_gpr3, ptrLpPaca->saved_gpr4);
|
|
printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->saved_gpr5);
|
|
|
|
printf(" Local Processor Register Save Area (LpRegSave): \n");
|
|
ptrLpRegSave = ptrPaca->reg_save_ptr;
|
|
printf(" Saved Sprg0=%.16lx Saved Sprg1=%.16lx \n",
|
|
ptrLpRegSave->xSPRG0, ptrLpRegSave->xSPRG0);
|
|
printf(" Saved Sprg2=%.16lx Saved Sprg3=%.16lx \n",
|
|
ptrLpRegSave->xSPRG2, ptrLpRegSave->xSPRG3);
|
|
printf(" Saved Msr =%.16lx Saved Nia =%.16lx \n",
|
|
ptrLpRegSave->xMSR, ptrLpRegSave->xNIA);
|
|
#endif
|
|
|
|
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;
|
|
case 'm':
|
|
val = get_msr();
|
|
scanhex(&val);
|
|
set_msrd(val);
|
|
break;
|
|
}
|
|
scannl();
|
|
}
|
|
|
|
/*
|
|
* Stuff for reading and writing memory safely
|
|
*/
|
|
int
|
|
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:
|
|
*(short *)q = *(short *)p;
|
|
break;
|
|
case 4:
|
|
*(int *)q = *(int *)p;
|
|
break;
|
|
case 8:
|
|
*(long *)q = *(long *)p;
|
|
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;
|
|
}
|
|
|
|
int
|
|
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:
|
|
*(short *)p = *(short *)q;
|
|
break;
|
|
case 4:
|
|
*(int *)p = *(int *)q;
|
|
break;
|
|
case 8:
|
|
*(long *)p = *(long *)q;
|
|
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 {
|
|
printf("*** Error writing address %x\n", adrs + n);
|
|
}
|
|
catch_memory_errors = 0;
|
|
return n;
|
|
}
|
|
|
|
static int fault_type;
|
|
static char *fault_chars[] = { "--", "**", "##" };
|
|
|
|
static int
|
|
handle_fault(struct pt_regs *regs)
|
|
{
|
|
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))
|
|
|
|
void
|
|
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;
|
|
|
|
static char *memex_help_string =
|
|
"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"
|
|
"";
|
|
|
|
static char *memex_subcmd_help_string =
|
|
"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"
|
|
"";
|
|
|
|
void
|
|
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);
|
|
printf("%.16x%c", adrs, brev? 'r': ' ');
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
int
|
|
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;
|
|
}
|
|
|
|
#define isxdigit(c) (('0' <= (c) && (c) <= '9') \
|
|
|| ('a' <= (c) && (c) <= 'f') \
|
|
|| ('A' <= (c) && (c) <= 'F'))
|
|
void
|
|
dump(void)
|
|
{
|
|
int c;
|
|
|
|
c = inchar();
|
|
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";
|
|
} 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";
|
|
}
|
|
}
|
|
|
|
void
|
|
prdump(unsigned long adrs, long ndump)
|
|
{
|
|
long n, m, c, r, nr;
|
|
unsigned char temp[16];
|
|
|
|
for (n = ndump; n > 0;) {
|
|
printf("%.16lx", adrs);
|
|
putchar(' ');
|
|
r = n < 16? n: 16;
|
|
nr = mread(adrs, temp, r);
|
|
adrs += nr;
|
|
for (m = 0; m < r; ++m) {
|
|
if ((m & 7) == 0 && m > 0)
|
|
putchar(' ');
|
|
if (m < nr)
|
|
printf("%.2x", temp[m]);
|
|
else
|
|
printf("%s", fault_chars[fault_type]);
|
|
}
|
|
if (m <= 8)
|
|
printf(" ");
|
|
for (; m < 16; ++m)
|
|
printf(" ");
|
|
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;
|
|
}
|
|
}
|
|
|
|
int
|
|
ppc_inst_dump(unsigned long adr, long count, int praddr)
|
|
{
|
|
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];
|
|
printf("%.16lx %s%s%s%s\n", adr, x, x, x, x);
|
|
}
|
|
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)
|
|
printf("%.16lx %.8x", adr, inst);
|
|
printf("\t");
|
|
print_insn_powerpc(inst, adr, 0); /* always returns 4 */
|
|
printf("\n");
|
|
}
|
|
return adr - first_adr;
|
|
}
|
|
|
|
void
|
|
print_address(unsigned long addr)
|
|
{
|
|
xmon_print_symbol(addr, "\t# ", "");
|
|
}
|
|
|
|
|
|
/*
|
|
* 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 */
|
|
|
|
void
|
|
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;
|
|
}
|
|
}
|
|
|
|
void
|
|
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;
|
|
|
|
void
|
|
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;
|
|
|
|
void
|
|
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);
|
|
fflush(stdout);
|
|
} else if (!ok && ook)
|
|
printf("%.8x\n", a - mskip);
|
|
ook = ok;
|
|
if (a + mskip < a)
|
|
break;
|
|
}
|
|
if (ook)
|
|
printf("%.8x\n", a - mskip);
|
|
}
|
|
|
|
/* 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",
|
|
"pc", "msr", "or3", "ctr", "lr", "xer", "ccr", "softe",
|
|
"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();
|
|
if (isspace(c)) {
|
|
termch = c;
|
|
break;
|
|
}
|
|
tmpstr[i] = c;
|
|
}
|
|
tmpstr[i++] = 0;
|
|
*vp = 0;
|
|
if (setjmp(bus_error_jmp) == 0) {
|
|
catch_memory_errors = 1;
|
|
sync();
|
|
*vp = kallsyms_lookup_name(tmpstr);
|
|
sync();
|
|
}
|
|
catch_memory_errors = 0;
|
|
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;
|
|
}
|
|
|
|
void
|
|
scannl(void)
|
|
{
|
|
int c;
|
|
|
|
c = termch;
|
|
termch = 0;
|
|
while( c != '\n' )
|
|
c = inchar();
|
|
}
|
|
|
|
int
|
|
hexdigit(int c)
|
|
{
|
|
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;
|
|
|
|
void
|
|
flush_input(void)
|
|
{
|
|
lineptr = NULL;
|
|
}
|
|
|
|
int
|
|
inchar(void)
|
|
{
|
|
if (lineptr == NULL || *lineptr == 0) {
|
|
if (fgets(line, sizeof(line), stdin) == NULL) {
|
|
lineptr = NULL;
|
|
return EOF;
|
|
}
|
|
lineptr = line;
|
|
}
|
|
return *lineptr++;
|
|
}
|
|
|
|
void
|
|
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;
|
|
|
|
printf("%.16lx", address);
|
|
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);
|
|
}
|
|
|
|
static void debug_trace(void)
|
|
{
|
|
unsigned long val, cmd, on;
|
|
|
|
cmd = skipbl();
|
|
if (cmd == '\n') {
|
|
/* show current state */
|
|
unsigned long i;
|
|
printf("ppc64_debug_switch = 0x%lx\n", ppc64_debug_switch);
|
|
for (i = 0; i < PPCDBG_NUM_FLAGS ;i++) {
|
|
on = PPCDBG_BITVAL(i) & ppc64_debug_switch;
|
|
printf("%02x %s %12s ", i, on ? "on " : "off", trace_names[i] ? trace_names[i] : "");
|
|
if (((i+1) % 3) == 0)
|
|
printf("\n");
|
|
}
|
|
printf("\n");
|
|
return;
|
|
}
|
|
while (cmd != '\n') {
|
|
on = 1; /* default if no sign given */
|
|
while (cmd == '+' || cmd == '-') {
|
|
on = (cmd == '+');
|
|
cmd = inchar();
|
|
if (cmd == ' ' || cmd == '\n') { /* Turn on or off based on + or - */
|
|
ppc64_debug_switch = on ? PPCDBG_ALL:PPCDBG_NONE;
|
|
printf("Setting all values to %s...\n", on ? "on" : "off");
|
|
if (cmd == '\n') return;
|
|
else cmd = skipbl();
|
|
}
|
|
else
|
|
termch = cmd;
|
|
}
|
|
termch = cmd; /* not +/- ... let scanhex see it */
|
|
scanhex((void *)&val);
|
|
if (val >= 64) {
|
|
printf("Value %x out of range:\n", val);
|
|
return;
|
|
}
|
|
if (on) {
|
|
ppc64_debug_switch |= PPCDBG_BITVAL(val);
|
|
printf("enable debug %x %s\n", val, trace_names[val] ? trace_names[val] : "");
|
|
} else {
|
|
ppc64_debug_switch &= ~PPCDBG_BITVAL(val);
|
|
printf("disable debug %x %s\n", val, trace_names[val] ? trace_names[val] : "");
|
|
}
|
|
cmd = skipbl();
|
|
}
|
|
}
|
|
|
|
static void dump_slb(void)
|
|
{
|
|
int i;
|
|
unsigned long tmp;
|
|
|
|
printf("SLB contents of cpu %x\n", smp_processor_id());
|
|
|
|
for (i = 0; i < SLB_NUM_ENTRIES; i++) {
|
|
asm volatile("slbmfee %0,%1" : "=r" (tmp) : "r" (i));
|
|
printf("%02d %016lx ", i, tmp);
|
|
|
|
asm volatile("slbmfev %0,%1" : "=r" (tmp) : "r" (i));
|
|
printf("%016lx\n", tmp);
|
|
}
|
|
}
|
|
|
|
static void dump_stab(void)
|
|
{
|
|
int i;
|
|
unsigned long *tmp = (unsigned long *)get_paca()->stab_addr;
|
|
|
|
printf("Segment table contents of cpu %x\n", smp_processor_id());
|
|
|
|
for (i = 0; i < PAGE_SIZE/16; i++) {
|
|
unsigned long a, b;
|
|
|
|
a = *tmp++;
|
|
b = *tmp++;
|
|
|
|
if (a || b) {
|
|
printf("%03d %016lx ", i, a);
|
|
printf("%016lx\n", b);
|
|
}
|
|
}
|
|
}
|
|
|
|
void xmon_init(void)
|
|
{
|
|
__debugger = xmon;
|
|
__debugger_ipi = xmon_ipi;
|
|
__debugger_bpt = xmon_bpt;
|
|
__debugger_sstep = xmon_sstep;
|
|
__debugger_iabr_match = xmon_iabr_match;
|
|
__debugger_dabr_match = xmon_dabr_match;
|
|
__debugger_fault_handler = xmon_fault_handler;
|
|
}
|
|
|
|
void dump_segments(void)
|
|
{
|
|
if (cpu_has_feature(CPU_FTR_SLB))
|
|
dump_slb();
|
|
else
|
|
dump_stab();
|
|
}
|