2005-04-17 02:20:36 +04:00
/*
* Routines providing a simple monitor for use on the PowerMac .
*
2005-11-08 14:55:08 +03:00
* Copyright ( C ) 1996 - 2005 Paul Mackerras .
2005-04-17 02:20:36 +04: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/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>
2005-10-28 16:53:37 +04:00
# include <linux/module.h>
2005-11-08 14:55:08 +03:00
# include <linux/sysrq.h>
2005-04-17 02:20:36 +04:00
# include <asm/ptrace.h>
# include <asm/string.h>
# include <asm/prom.h>
# include <asm/machdep.h>
2005-10-28 16:53:37 +04:00
# include <asm/xmon.h>
# ifdef CONFIG_PMAC_BACKLIGHT
# include <asm/backlight.h>
# endif
2005-04-17 02:20:36 +04: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>
# include <asm/bug.h>
2005-10-28 16:53:37 +04:00
# ifdef CONFIG_PPC64
2005-04-17 02:20:36 +04:00
# include <asm/hvcall.h>
2005-10-28 16:53:37 +04:00
# include <asm/paca.h>
# endif
2005-04-17 02:20:36 +04:00
# include "nonstdio.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 ] ;
2005-10-28 16:53:37 +04:00
# define JMP_BUF_LEN 23
2005-04-17 02:20:36 +04:00
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 ) ;
2005-10-28 16:53:37 +04:00
static void proccall ( void ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
extern int print_insn_powerpc ( unsigned long , unsigned long , int ) ;
2005-10-28 16:53:37 +04:00
extern void xmon_enter ( void ) ;
extern void xmon_leave ( void ) ;
extern long setjmp ( long * ) ;
extern void longjmp ( long * , long ) ;
extern void xmon_save_regs ( struct pt_regs * ) ;
# ifdef CONFIG_PPC64
# define REG "%.16lx"
# define REGS_PER_LINE 4
# define LAST_VOLATILE 13
# else
# define REG "%.8lx"
# define REGS_PER_LINE 8
# define LAST_VOLATILE 12
# endif
2005-04-17 02:20:36 +04:00
# 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 \
2005-10-28 16:53:37 +04:00
p call a procedure \ n \
2005-04-17 02:20:36 +04:00
r print registers \ n \
s single step \ n \
S print special registers \ n \
t print backtrace \ n \
x exit monitor and recover \ n \
2005-10-28 16:53:37 +04:00
X exit monitor and dont recover \ n "
# ifdef CONFIG_PPC64
" u dump segment table or SLB \n "
# endif
# ifdef CONFIG_PPC_STD_MMU_32
" u dump segment registers \n "
# endif
" ? help \n "
" zr reboot \n \
2005-04-17 02:20:36 +04:00
zh halt \ n "
;
static struct pt_regs * xmon_regs ;
2005-10-28 16:53:37 +04:00
static inline void sync ( void )
2005-04-17 02:20:36 +04:00
{
asm volatile ( " sync; isync " ) ;
}
2005-10-28 16:53:37 +04: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 02:20:36 +04: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 ;
/*
* 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
2005-10-28 16:53:37 +04:00
msr = mfmsr ( ) ;
mtmsr ( msr & ~ MSR_EE ) ; /* disable interrupts */
2005-04-17 02:20:36 +04: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 ( ) ;
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 ) ) ) ;
2005-08-03 09:08:18 +04:00
release_output_lock ( ) ;
2005-04-17 02:20:36 +04:00
longjmp ( xmon_fault_jmp [ cpu ] , 1 ) ;
}
if ( setjmp ( recurse_jmp ) ! = 0 ) {
if ( ! in_xmon | | ! xmon_gate ) {
2005-08-03 09:08:18 +04:00
get_output_lock ( ) ;
2005-04-17 02:20:36 +04:00
printf ( " xmon: WARNING: bad recursive fault "
" on cpu 0x%x \n " , cpu ) ;
2005-08-03 09:08:18 +04:00
release_output_lock ( ) ;
2005-04-17 02:20:36 +04:00
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 ( ) ;
2005-10-28 16:53:37 +04:00
mtmsr ( msr ) ; /* restore interrupt enable */
2005-04-17 02:20:36 +04:00
return cmd ! = ' X ' ;
}
int xmon ( struct pt_regs * excp )
{
struct pt_regs regs ;
if ( excp = = NULL ) {
2005-10-28 16:53:37 +04:00
xmon_save_regs ( & regs ) ;
2005-04-17 02:20:36 +04:00
excp = & regs ;
}
return xmon_core ( excp , 0 ) ;
}
2005-10-28 16:53:37 +04:00
EXPORT_SYMBOL ( xmon ) ;
irqreturn_t
xmon_irq ( int irq , void * d , struct pt_regs * regs )
{
unsigned long flags ;
local_irq_save ( flags ) ;
printf ( " Keyboard interrupt \n " ) ;
xmon ( regs ) ;
local_irq_restore ( flags ) ;
return IRQ_HANDLED ;
}
2005-04-17 02:20:36 +04:00
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 ;
2005-09-10 10:01:11 +04:00
if ( dabr . enabled = = 0 )
return 0 ;
2005-04-17 02:20:36 +04:00
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 ;
}
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 )
2005-09-10 10:01:11 +04:00
set_dabr ( dabr . address | ( dabr . enabled & 7 ) ) ;
2005-04-17 02:20:36 +04:00
if ( iabr & & cpu_has_feature ( CPU_FTR_IABR ) )
2005-10-28 16:53:37 +04:00
mtspr ( SPRN_IABR , iabr - > address
2005-04-17 02:20:36 +04:00
| ( 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 )
{
2005-09-10 10:01:11 +04:00
set_dabr ( 0 ) ;
2005-04-17 02:20:36 +04:00
if ( cpu_has_feature ( CPU_FTR_IABR ) )
2005-10-28 16:53:37 +04:00
mtspr ( SPRN_IABR , 0 ) ;
2005-04-17 02:20:36 +04:00
}
/* 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> " ) ;
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 ' b ' :
bpt_cmds ( ) ;
break ;
case ' C ' :
csum ( ) ;
break ;
case ' c ' :
if ( cpu_cmd ( ) )
return 0 ;
break ;
case ' z ' :
bootcmds ( ) ;
break ;
2005-10-28 16:53:37 +04:00
case ' p ' :
proccall ( ) ;
2005-04-17 02:20:36 +04:00
break ;
2005-10-28 16:53:37 +04:00
# ifdef CONFIG_PPC_STD_MMU
2005-04-17 02:20:36 +04:00
case ' u ' :
dump_segments ( ) ;
break ;
2005-10-28 16:53:37 +04:00
# endif
2005-04-17 02:20:36 +04:00
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 ) {
2005-10-28 16:53:37 +04:00
# ifndef CONFIG_8xx
2005-04-17 02:20:36 +04: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 ) ) {
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 ;
2005-10-28 16:53:37 +04:00
# endif
2005-04-17 02:20:36 +04: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 ) ;
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 ) {
2005-10-28 16:53:37 +04:00
printf ( " data " REG " [ " , dabr . address ) ;
2005-04-17 02:20:36 +04: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 ) ,
( 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 ;
2005-10-28 16:53:37 +04:00
# ifdef CONFIG_PPC64
# define LRSAVE_OFFSET 0x10
# define REG_FRAME_MARKER 0x7265677368657265ul /* "regshere" */
# define MARKER_OFFSET 0x60
# define REGS_OFFSET 0x70
# else
# define LRSAVE_OFFSET 4
# define REG_FRAME_MARKER 0x72656773
# define MARKER_OFFSET 8
# define REGS_OFFSET 16
# endif
2005-04-17 02:20:36 +04:00
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 ;
}
2005-10-28 16:53:37 +04:00
if ( ! mread ( sp + LRSAVE_OFFSET , & ip , sizeof ( unsigned long ) )
2005-04-17 02:20:36 +04: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 16:53:37 +04:00
mread ( newsp + LRSAVE_OFFSET , & nextip ,
2005-04-17 02:20:36 +04: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 16:53:37 +04:00
printf ( " [ " REG " ] " , sp ) ;
2005-04-17 02:20:36 +04:00
xmon_print_symbol ( ip , " " , " (unreliable) \n " ) ;
}
pc = lr = 0 ;
} else {
2005-10-28 16:53:37 +04:00
printf ( " [ " REG " ] " , sp ) ;
2005-04-17 02:20:36 +04:00
xmon_print_symbol ( ip , " " , " \n " ) ;
}
/* Look for "regshere" marker to see if this is
an exception frame . */
2005-10-28 16:53:37 +04:00
if ( mread ( sp + MARKER_OFFSET , & marker , sizeof ( unsigned long ) )
& & marker = = REG_FRAME_MARKER ) {
if ( mread ( sp + REGS_OFFSET , & regs , sizeof ( regs ) )
2005-04-17 02:20:36 +04:00
! = sizeof ( regs ) ) {
printf ( " Couldn't read registers at %lx \n " ,
2005-10-28 16:53:37 +04:00
sp + REGS_OFFSET ) ;
2005-04-17 02:20:36 +04:00
break ;
}
printf ( " --- Exception: %lx %s at " , regs . trap ,
getvecname ( TRAP ( & regs ) ) ) ;
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 ) ;
2005-10-28 16:53:37 +04:00
# ifdef CONFIG_PPC64
2005-04-17 02:20:36 +04:00
printf ( " paca = 0x%lx \n " , get_paca ( ) ) ;
2005-10-28 16:53:37 +04:00
# endif
2005-04-17 02:20:36 +04:00
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 )
{
2005-10-28 16:53:37 +04:00
int n , trap ;
2005-04-17 02:20:36 +04: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 16:53:37 +04:00
printf ( " *** Error reading registers from " REG " \n " ,
2005-04-17 02:20:36 +04:00
base ) ;
return ;
}
catch_memory_errors = 0 ;
fp = & regs ;
}
2005-10-28 16:53:37 +04:00
# ifdef CONFIG_PPC64
2005-04-17 02:20:36 +04:00
if ( FULL_REGS ( fp ) ) {
for ( n = 0 ; n < 16 ; + + n )
2005-10-28 16:53:37 +04:00
printf ( " R%.2ld = " REG " R%.2ld = " REG " \n " ,
2005-04-17 02:20:36 +04:00
n , fp - > gpr [ n ] , n + 16 , fp - > gpr [ n + 16 ] ) ;
} else {
for ( n = 0 ; n < 7 ; + + n )
2005-10-28 16:53:37 +04:00
printf ( " R%.2ld = " REG " R%.2ld = " REG " \n " ,
2005-04-17 02:20:36 +04:00
n , fp - > gpr [ n ] , n + 7 , fp - > gpr [ n + 7 ] ) ;
}
2005-10-28 16:53:37 +04: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 02:20:36 +04:00
printf ( " pc = " ) ;
xmon_print_symbol ( fp - > nip , " " , " \n " ) ;
printf ( " lr = " ) ;
xmon_print_symbol ( fp - > link , " " , " \n " ) ;
2005-10-28 16:53:37 +04:00
printf ( " msr = " REG " cr = %.8lx \n " , fp - > msr , fp - > ccr ) ;
printf ( " ctr = " REG " xer = " REG " trap = %4lx \n " ,
2005-04-17 02:20:36 +04:00
fp - > ctr , fp - > xer , fp - > trap ) ;
2005-10-28 16:53:37 +04:00
trap = TRAP ( fp ) ;
if ( trap = = 0x300 | | trap = = 0x380 | | trap = = 0x600 )
printf ( " dar = " REG " dsisr = %.8lx \n " , fp - > dar , fp - > dsisr ) ;
2005-04-17 02:20:36 +04:00
}
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 ;
2005-10-28 16:53:37 +04:00
void super_regs ( void )
2005-04-17 02:20:36 +04:00
{
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 ) : ) ;
2005-10-28 16:53:37 +04:00
printf ( " msr = " REG " sprg0= " REG " \n " ,
mfmsr ( ) , mfspr ( SPRN_SPRG0 ) ) ;
printf ( " pvr = " REG " sprg1= " REG " \n " ,
mfspr ( SPRN_PVR ) , mfspr ( SPRN_SPRG1 ) ) ;
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 02:20:36 +04:00
# 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 ( & regno ) ;
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
*/
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 :
2005-10-28 16:53:37 +04:00
* ( u16 * ) q = * ( u16 * ) p ;
2005-04-17 02:20:36 +04:00
break ;
case 4 :
2005-10-28 16:53:37 +04:00
* ( u32 * ) q = * ( u32 * ) p ;
2005-04-17 02:20:36 +04:00
break ;
case 8 :
2005-10-28 16:53:37 +04:00
* ( u64 * ) q = * ( u64 * ) p ;
2005-04-17 02:20:36 +04: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 ;
}
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 :
2005-10-28 16:53:37 +04:00
* ( u16 * ) p = * ( u16 * ) q ;
2005-04-17 02:20:36 +04:00
break ;
case 4 :
2005-10-28 16:53:37 +04:00
* ( u32 * ) p = * ( u32 * ) q ;
2005-04-17 02:20:36 +04:00
break ;
case 8 :
2005-10-28 16:53:37 +04:00
* ( u64 * ) p = * ( u64 * ) q ;
2005-04-17 02:20:36 +04: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 {
printf ( " *** Error writing address %x \n " , adrs + n ) ;
}
catch_memory_errors = 0 ;
return n ;
}
static int fault_type ;
2005-10-28 16:53:37 +04:00
static int fault_except ;
2005-04-17 02:20:36 +04:00
static char * fault_chars [ ] = { " -- " , " ** " , " ## " } ;
2005-10-28 16:53:37 +04:00
static int handle_fault ( struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2005-10-28 16:53:37 +04:00
fault_except = TRAP ( regs ) ;
2005-04-17 02:20:36 +04: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))
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 ) ;
2005-11-10 06:30:20 +03:00
printf ( REG " %c " , adrs , brev ? ' r ' : ' ' ) ;
2005-04-17 02:20:36 +04: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 ;
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 ; ) {
2005-10-28 16:53:37 +04:00
printf ( REG , adrs ) ;
2005-04-17 02:20:36 +04:00
putchar ( ' ' ) ;
r = n < 16 ? n : 16 ;
nr = mread ( adrs , temp , r ) ;
adrs + = nr ;
for ( m = 0 ; m < r ; + + m ) {
2005-11-10 06:30:20 +03:00
if ( ( m & ( sizeof ( long ) - 1 ) ) = = 0 & & m > 0 )
putchar ( ' ' ) ;
2005-04-17 02:20:36 +04:00
if ( m < nr )
printf ( " %.2x " , temp [ m ] ) ;
else
printf ( " %s " , fault_chars [ fault_type ] ) ;
}
2005-11-10 06:30:20 +03:00
for ( ; m < 16 ; + + m ) {
if ( ( m & ( sizeof ( long ) - 1 ) ) = = 0 )
putchar ( ' ' ) ;
2005-04-17 02:20:36 +04:00
printf ( " " ) ;
2005-11-10 06:30:20 +03:00
}
2005-04-17 02:20:36 +04: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 ;
}
}
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 ] ;
2005-10-28 16:53:37 +04:00
printf ( REG " %s%s%s%s \n " , adr , x , x , x , x ) ;
2005-04-17 02:20:36 +04: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 16:53:37 +04:00
printf ( REG " %.8x " , adr , inst ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
} else if ( ! ok & & ook )
printf ( " %.8x \n " , a - mskip ) ;
ook = ok ;
if ( a + mskip < a )
break ;
}
if ( ook )
printf ( " %.8x \n " , a - mskip ) ;
}
2005-10-28 16:53:37 +04:00
void proccall ( void )
{
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 ( ) ;
printf ( " return value is %x \n " , ret ) ;
} else {
printf ( " *** %x exception occurred \n " , fault_except ) ;
}
catch_memory_errors = 0 ;
}
2005-04-17 02:20:36 +04: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 16:53:37 +04:00
" pc " , " msr " , " or3 " , " ctr " , " lr " , " xer " , " ccr " ,
# ifdef CONFIG_PPC64
" softe " ,
# else
" mq " ,
# endif
2005-04-17 02:20:36 +04: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 ( ) ;
if ( isspace ( c ) ) {
termch = c ;
break ;
}
tmpstr [ i ] = c ;
}
tmpstr [ i + + ] = 0 ;
2005-06-22 04:15:30 +04: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 02:20:36 +04: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 ;
}
void
scannl ( void )
{
int c ;
c = termch ;
termch = 0 ;
while ( c ! = ' \n ' )
c = inchar ( ) ;
}
2005-10-28 16:53:37 +04:00
int hexdigit ( int c )
2005-04-17 02:20:36 +04: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 ;
void
flush_input ( void )
{
lineptr = NULL ;
}
int
inchar ( void )
{
if ( lineptr = = NULL | | * lineptr = = 0 ) {
2005-11-08 14:55:08 +03:00
if ( xmon_gets ( line , sizeof ( line ) ) = = NULL ) {
2005-04-17 02:20:36 +04:00
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 ;
2005-10-28 16:53:37 +04:00
printf ( REG , address ) ;
2005-04-17 02:20:36 +04: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 ) ;
}
2005-10-28 16:53:37 +04:00
# ifdef CONFIG_PPC64
2005-04-17 02:20:36 +04:00
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 ) ;
}
}
}
2005-10-28 16:53:37 +04:00
void dump_segments ( void )
{
if ( cpu_has_feature ( CPU_FTR_SLB ) )
dump_slb ( ) ;
else
dump_stab ( ) ;
}
# 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
2005-08-04 21:26:42 +04:00
void xmon_init ( int enable )
{
if ( enable ) {
__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 ;
} else {
__debugger = NULL ;
__debugger_ipi = NULL ;
__debugger_bpt = NULL ;
__debugger_sstep = NULL ;
__debugger_iabr_match = NULL ;
__debugger_dabr_match = NULL ;
__debugger_fault_handler = NULL ;
}
2005-11-08 14:55:08 +03:00
xmon_map_scc ( ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-08 14:55:08 +03:00
# ifdef CONFIG_MAGIC_SYSRQ
static void sysrq_handle_xmon ( int key , struct pt_regs * pt_regs ,
struct tty_struct * tty )
{
/* ensure xmon is enabled */
xmon_init ( 1 ) ;
debugger ( pt_regs ) ;
}
static struct sysrq_key_op sysrq_xmon_op =
{
. handler = sysrq_handle_xmon ,
. help_msg = " Xmon " ,
. 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 */