2008-09-30 15:12:15 +04:00
/*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
* Copyright ( C ) 2000 , 2001 , 2002 Andi Kleen , SuSE Labs
*/
# include <linux/kallsyms.h>
# include <linux/kprobes.h>
# include <linux/uaccess.h>
# include <linux/hardirq.h>
# include <linux/kdebug.h>
# include <linux/module.h>
# include <linux/ptrace.h>
# include <linux/kexec.h>
# include <linux/bug.h>
# include <linux/nmi.h>
2007-08-25 03:11:54 +04:00
# include <linux/sysfs.h>
2008-09-30 15:12:15 +04:00
# include <asm/stacktrace.h>
2008-10-23 18:40:06 +04:00
# include "dumpstack.h"
2008-09-30 15:12:15 +04:00
2009-07-01 23:02:09 +04:00
static char x86_stack_ids [ ] [ 8 ] = {
2008-09-30 15:12:15 +04:00
[ DEBUG_STACK - 1 ] = " #DB " ,
[ NMI_STACK - 1 ] = " NMI " ,
[ DOUBLEFAULT_STACK - 1 ] = " #DF " ,
[ STACKFAULT_STACK - 1 ] = " #SS " ,
[ MCE_STACK - 1 ] = " #MC " ,
# if DEBUG_STKSZ > EXCEPTION_STKSZ
[ N_EXCEPTION_STACKS . . .
N_EXCEPTION_STACKS + DEBUG_STKSZ / EXCEPTION_STKSZ - 2 ] = " #DB[?] "
# endif
} ;
2009-07-01 23:02:09 +04:00
int x86_is_stack_id ( int id , char * name )
{
return x86_stack_ids [ id - 1 ] = = name ;
}
static unsigned long * in_exception_stack ( unsigned cpu , unsigned long stack ,
unsigned * usedp , char * * idp )
{
2008-09-30 15:12:15 +04:00
unsigned k ;
/*
* Iterate over all exception stacks , and figure out whether
* ' stack ' is in one of them :
*/
for ( k = 0 ; k < N_EXCEPTION_STACKS ; k + + ) {
unsigned long end = per_cpu ( orig_ist , cpu ) . ist [ k ] ;
/*
* Is ' stack ' above this exception frame ' s end ?
* If yes then skip to the next frame .
*/
if ( stack > = end )
continue ;
/*
* Is ' stack ' above this exception frame ' s start address ?
* If yes then we found the right frame .
*/
if ( stack > = end - EXCEPTION_STKSZ ) {
/*
* Make sure we only iterate through an exception
* stack once . If it comes up for the second time
* then there ' s something wrong going on - just
* break out and return NULL :
*/
if ( * usedp & ( 1U < < k ) )
break ;
* usedp | = 1U < < k ;
2009-07-01 23:02:09 +04:00
* idp = x86_stack_ids [ k ] ;
2008-09-30 15:12:15 +04:00
return ( unsigned long * ) end ;
}
/*
* If this is a debug stack , and if it has a larger size than
* the usual exception stacks , then ' stack ' might still
* be within the lower portion of the debug stack :
*/
# if DEBUG_STKSZ > EXCEPTION_STKSZ
if ( k = = DEBUG_STACK - 1 & & stack > = end - DEBUG_STKSZ ) {
unsigned j = N_EXCEPTION_STACKS - 1 ;
/*
* Black magic . A large debug stack is composed of
* multiple exception stack entries , which we
* iterate through now . Dont look :
*/
do {
+ + j ;
end - = EXCEPTION_STKSZ ;
2009-07-01 23:02:09 +04:00
x86_stack_ids [ j ] [ 4 ] = ' 1 ' +
( j - N_EXCEPTION_STACKS ) ;
2008-09-30 15:12:15 +04:00
} while ( stack < end - EXCEPTION_STKSZ ) ;
if ( * usedp & ( 1U < < j ) )
break ;
* usedp | = 1U < < j ;
2009-07-01 23:02:09 +04:00
* idp = x86_stack_ids [ j ] ;
2008-09-30 15:12:15 +04:00
return ( unsigned long * ) end ;
}
# endif
}
return NULL ;
}
/*
* x86 - 64 can have up to three kernel stacks :
* process stack
* interrupt stack
* severe exception ( double fault , nmi , stack fault , debug , mce ) hardware stack
*/
void dump_trace ( struct task_struct * task , struct pt_regs * regs ,
unsigned long * stack , unsigned long bp ,
const struct stacktrace_ops * ops , void * data )
{
const unsigned cpu = get_cpu ( ) ;
2009-01-18 18:38:58 +03:00
unsigned long * irq_stack_end =
( unsigned long * ) per_cpu ( irq_stack_ptr , cpu ) ;
2008-09-30 15:12:15 +04:00
unsigned used = 0 ;
struct thread_info * tinfo ;
2008-12-03 07:50:04 +03:00
int graph = 0 ;
2008-09-30 15:12:15 +04:00
if ( ! task )
task = current ;
if ( ! stack ) {
unsigned long dummy ;
stack = & dummy ;
if ( task & & task ! = current )
stack = ( unsigned long * ) task - > thread . sp ;
}
# ifdef CONFIG_FRAME_POINTER
if ( ! bp ) {
if ( task = = current ) {
/* Grab bp right from our regs */
2008-10-05 01:12:46 +04:00
get_bp ( bp ) ;
2008-09-30 15:12:15 +04:00
} else {
/* bp is the last reg pushed by switch_to */
bp = * ( unsigned long * ) task - > thread . sp ;
}
}
# endif
/*
* Print function call entries in all stacks , starting at the
* current stack address . If the stacks consist of nested
* exceptions
*/
tinfo = task_thread_info ( task ) ;
for ( ; ; ) {
char * id ;
unsigned long * estack_end ;
estack_end = in_exception_stack ( cpu , ( unsigned long ) stack ,
& used , & id ) ;
if ( estack_end ) {
if ( ops - > stack ( data , id ) < 0 )
break ;
bp = print_context_stack ( tinfo , stack , bp , ops ,
2008-12-03 07:50:04 +03:00
data , estack_end , & graph ) ;
2008-09-30 15:12:15 +04:00
ops - > stack ( data , " <EOE> " ) ;
/*
* We link to the next stack via the
* second - to - last pointer ( index - 2 to end ) in the
* exception stack :
*/
stack = ( unsigned long * ) estack_end [ - 2 ] ;
continue ;
}
2009-01-18 18:38:58 +03:00
if ( irq_stack_end ) {
unsigned long * irq_stack ;
irq_stack = irq_stack_end -
( IRQ_STACK_SIZE - 64 ) / sizeof ( * irq_stack ) ;
2008-09-30 15:12:15 +04:00
2009-01-18 18:38:58 +03:00
if ( stack > = irq_stack & & stack < irq_stack_end ) {
2008-09-30 15:12:15 +04:00
if ( ops - > stack ( data , " IRQ " ) < 0 )
break ;
bp = print_context_stack ( tinfo , stack , bp ,
2009-01-18 18:38:58 +03:00
ops , data , irq_stack_end , & graph ) ;
2008-09-30 15:12:15 +04:00
/*
* We link to the next stack ( which would be
* the process stack normally ) the last
* pointer ( index - 1 to end ) in the IRQ stack :
*/
2009-01-18 18:38:58 +03:00
stack = ( unsigned long * ) ( irq_stack_end [ - 1 ] ) ;
irq_stack_end = NULL ;
2008-09-30 15:12:15 +04:00
ops - > stack ( data , " EOI " ) ;
continue ;
}
}
break ;
}
/*
* This handles the process stack :
*/
2008-12-03 07:50:04 +03:00
bp = print_context_stack ( tinfo , stack , bp , ops , data , NULL , & graph ) ;
2008-09-30 15:12:15 +04:00
put_cpu ( ) ;
}
EXPORT_SYMBOL ( dump_trace ) ;
2008-10-23 18:40:06 +04:00
void
2008-09-30 15:12:15 +04:00
show_stack_log_lvl ( struct task_struct * task , struct pt_regs * regs ,
unsigned long * sp , unsigned long bp , char * log_lvl )
{
unsigned long * stack ;
int i ;
const int cpu = smp_processor_id ( ) ;
2009-01-18 18:38:58 +03:00
unsigned long * irq_stack_end =
( unsigned long * ) ( per_cpu ( irq_stack_ptr , cpu ) ) ;
unsigned long * irq_stack =
( unsigned long * ) ( per_cpu ( irq_stack_ptr , cpu ) - IRQ_STACK_SIZE ) ;
2008-09-30 15:12:15 +04:00
/*
* debugging aid : " show_stack(NULL, NULL); " prints the
* back trace for this cpu .
*/
if ( sp = = NULL ) {
if ( task )
sp = ( unsigned long * ) task - > thread . sp ;
else
sp = ( unsigned long * ) & sp ;
}
stack = sp ;
for ( i = 0 ; i < kstack_depth_to_print ; i + + ) {
2009-01-18 18:38:58 +03:00
if ( stack > = irq_stack & & stack < = irq_stack_end ) {
if ( stack = = irq_stack_end ) {
stack = ( unsigned long * ) ( irq_stack_end [ - 1 ] ) ;
2008-09-30 15:12:15 +04:00
printk ( " <EOI> " ) ;
}
} else {
if ( ( ( long ) stack & ( THREAD_SIZE - 1 ) ) = = 0 )
break ;
}
2008-10-05 01:12:46 +04:00
if ( i & & ( ( i % STACKSLOTS_PER_LINE ) = = 0 ) )
2008-10-05 01:12:44 +04:00
printk ( " \n %s " , log_lvl ) ;
2008-09-30 15:12:15 +04:00
printk ( " %016lx " , * stack + + ) ;
touch_nmi_watchdog ( ) ;
}
printk ( " \n " ) ;
show_trace_log_lvl ( task , regs , sp , bp , log_lvl ) ;
}
void show_registers ( struct pt_regs * regs )
{
int i ;
unsigned long sp ;
const int cpu = smp_processor_id ( ) ;
2009-01-18 18:38:58 +03:00
struct task_struct * cur = current ;
2008-09-30 15:12:15 +04:00
sp = regs - > sp ;
printk ( " CPU %d " , cpu ) ;
__show_regs ( regs , 1 ) ;
printk ( " Process %s (pid: %d, threadinfo %p, task %p) \n " ,
cur - > comm , cur - > pid , task_thread_info ( cur ) , cur ) ;
/*
* When in - kernel , we also print out the stack and code at the
* time of the fault . .
*/
if ( ! user_mode ( regs ) ) {
unsigned int code_prologue = code_bytes * 43 / 64 ;
unsigned int code_len = code_bytes ;
unsigned char c ;
u8 * ip ;
2008-10-05 01:12:44 +04:00
printk ( KERN_EMERG " Stack: \n " ) ;
2008-09-30 15:12:15 +04:00
show_stack_log_lvl ( NULL , regs , ( unsigned long * ) sp ,
2008-10-05 01:12:44 +04:00
regs - > bp , KERN_EMERG ) ;
2008-09-30 15:12:15 +04:00
printk ( KERN_EMERG " Code: " ) ;
ip = ( u8 * ) regs - > ip - code_prologue ;
if ( ip < ( u8 * ) PAGE_OFFSET | | probe_kernel_address ( ip , c ) ) {
2008-10-05 01:12:46 +04:00
/* try starting at IP */
2008-09-30 15:12:15 +04:00
ip = ( u8 * ) regs - > ip ;
code_len = code_len - code_prologue + 1 ;
}
for ( i = 0 ; i < code_len ; i + + , ip + + ) {
if ( ip < ( u8 * ) PAGE_OFFSET | |
probe_kernel_address ( ip , c ) ) {
printk ( " Bad RIP value. " ) ;
break ;
}
if ( ip = = ( u8 * ) regs - > ip )
printk ( " <%02x> " , c ) ;
else
printk ( " %02x " , c ) ;
}
}
printk ( " \n " ) ;
}
int is_valid_bugaddr ( unsigned long ip )
{
unsigned short ud2 ;
if ( __copy_from_user ( & ud2 , ( const void __user * ) ip , sizeof ( ud2 ) ) )
return 0 ;
return ud2 = = 0x0b0f ;
}