2007-11-20 18:08:06 +09:00
# include <linux/bug.h>
# include <linux/io.h>
# include <linux/types.h>
# include <linux/kdebug.h>
2007-11-26 18:17:51 +09:00
# include <linux/signal.h>
# include <linux/sched.h>
2008-05-19 19:32:07 +09:00
# include <linux/uaccess.h>
2009-09-01 17:38:32 +09:00
# include <linux/hardirq.h>
2012-06-14 14:18:51 +09:00
# include <linux/kernel.h>
# include <linux/kexec.h>
# include <linux/module.h>
2009-08-22 05:28:25 +09:00
# include <asm/unwinder.h>
2012-03-28 18:30:03 +01:00
# include <asm/traps.h>
2007-11-20 18:08:06 +09:00
2012-06-14 14:18:51 +09:00
static DEFINE_SPINLOCK ( die_lock ) ;
void die ( const char * str , struct pt_regs * regs , long err )
{
static int die_counter ;
oops_enter ( ) ;
spin_lock_irq ( & die_lock ) ;
console_verbose ( ) ;
bust_spinlocks ( 1 ) ;
printk ( " %s: %04lx [#%d] \n " , str , err & 0xffff , + + die_counter ) ;
print_modules ( ) ;
show_regs ( regs ) ;
printk ( " Process: %s (pid: %d, stack limit = %p) \n " , current - > comm ,
task_pid_nr ( current ) , task_stack_page ( current ) + 1 ) ;
if ( ! user_mode ( regs ) | | in_interrupt ( ) )
dump_mem ( " Stack: " , regs - > regs [ 15 ] , THREAD_SIZE +
( unsigned long ) task_stack_page ( current ) ) ;
notify_die ( DIE_OOPS , str , regs , err , 255 , SIGSEGV ) ;
bust_spinlocks ( 0 ) ;
add_taint ( TAINT_DIE ) ;
spin_unlock_irq ( & die_lock ) ;
oops_exit ( ) ;
if ( kexec_should_crash ( current ) )
crash_kexec ( regs ) ;
if ( in_interrupt ( ) )
panic ( " Fatal exception in interrupt " ) ;
if ( panic_on_oops )
panic ( " Fatal exception " ) ;
do_exit ( SIGSEGV ) ;
}
void die_if_kernel ( const char * str , struct pt_regs * regs , long err )
{
if ( ! user_mode ( regs ) )
die ( str , regs , err ) ;
}
/*
* try and fix up kernelspace address errors
* - userspace errors just cause EFAULT to be returned , resulting in SEGV
* - kernel / userspace interfaces cause a jump to an appropriate handler
* - other kernel errors are bad
*/
void die_if_no_fixup ( const char * str , struct pt_regs * regs , long err )
{
if ( ! user_mode ( regs ) ) {
const struct exception_table_entry * fixup ;
fixup = search_exception_tables ( regs - > pc ) ;
if ( fixup ) {
regs - > pc = fixup - > fixup ;
return ;
}
die ( str , regs , err ) ;
}
}
2009-11-12 16:39:47 +09:00
# ifdef CONFIG_GENERIC_BUG
static void handle_BUG ( struct pt_regs * regs )
2007-11-20 18:08:06 +09:00
{
2009-08-22 05:28:25 +09:00
const struct bug_entry * bug ;
unsigned long bugaddr = regs - > pc ;
2007-11-20 18:08:06 +09:00
enum bug_trap_type tt ;
2009-08-22 05:28:25 +09:00
if ( ! is_valid_bugaddr ( bugaddr ) )
goto invalid ;
bug = find_bug ( bugaddr ) ;
/* Switch unwinders when unwind_stack() is called */
if ( bug - > flags & BUGFLAG_UNWINDER )
unwinder_faulted = 1 ;
tt = report_bug ( bugaddr , regs ) ;
2007-11-20 18:08:06 +09:00
if ( tt = = BUG_TRAP_TYPE_WARN ) {
2009-08-22 05:28:25 +09:00
regs - > pc + = instruction_size ( bugaddr ) ;
2007-11-20 18:08:06 +09:00
return ;
}
2009-08-22 05:28:25 +09:00
invalid :
2007-11-20 18:08:06 +09:00
die ( " Kernel BUG " , regs , TRAPA_BUG_OPCODE & 0xff ) ;
}
int is_valid_bugaddr ( unsigned long addr )
{
2009-05-09 16:02:08 +09:00
insn_size_t opcode ;
2008-05-19 19:32:07 +09:00
if ( addr < PAGE_OFFSET )
return 0 ;
2009-05-09 16:02:08 +09:00
if ( probe_kernel_address ( ( insn_size_t * ) addr , opcode ) )
2008-05-19 19:32:07 +09:00
return 0 ;
2009-08-22 05:28:25 +09:00
if ( opcode = = TRAPA_BUG_OPCODE )
2009-08-16 21:54:48 +01:00
return 1 ;
return 0 ;
2007-11-20 18:08:06 +09:00
}
# endif
/*
* Generic trap handler .
*/
BUILD_TRAP_HANDLER ( debug )
{
TRAP_HANDLER_DECL ;
/* Rewind */
2010-01-26 12:58:40 +09:00
regs - > pc - = instruction_size ( __raw_readw ( regs - > pc - 4 ) ) ;
2007-11-20 18:08:06 +09:00
if ( notify_die ( DIE_TRAP , " debug trap " , regs , 0 , vec & 0xff ,
SIGTRAP ) = = NOTIFY_STOP )
return ;
force_sig ( SIGTRAP , current ) ;
}
/*
* Special handler for BUG ( ) traps .
*/
BUILD_TRAP_HANDLER ( bug )
{
TRAP_HANDLER_DECL ;
/* Rewind */
2010-01-26 12:58:40 +09:00
regs - > pc - = instruction_size ( __raw_readw ( regs - > pc - 4 ) ) ;
2007-11-20 18:08:06 +09:00
if ( notify_die ( DIE_TRAP , " bug trap " , regs , 0 , TRAPA_BUG_OPCODE & 0xff ,
SIGTRAP ) = = NOTIFY_STOP )
return ;
2009-11-12 16:39:47 +09:00
# ifdef CONFIG_GENERIC_BUG
2007-11-20 18:08:06 +09:00
if ( __kernel_text_address ( instruction_pointer ( regs ) ) ) {
2009-05-09 16:02:08 +09:00
insn_size_t insn = * ( insn_size_t * ) instruction_pointer ( regs ) ;
2007-11-20 18:08:06 +09:00
if ( insn = = TRAPA_BUG_OPCODE )
handle_BUG ( regs ) ;
2009-06-17 04:48:20 +00:00
return ;
2007-11-20 18:08:06 +09:00
}
# endif
force_sig ( SIGTRAP , current ) ;
}
2009-09-01 17:38:32 +09:00
BUILD_TRAP_HANDLER ( nmi )
{
2009-10-14 16:42:28 +09:00
unsigned int cpu = smp_processor_id ( ) ;
2009-09-01 17:38:32 +09:00
TRAP_HANDLER_DECL ;
nmi_enter ( ) ;
2009-10-14 16:42:28 +09:00
nmi_count ( cpu ) + + ;
2009-09-01 17:38:32 +09:00
switch ( notify_die ( DIE_NMI , " NMI " , regs , 0 , vec & 0xff , SIGINT ) ) {
case NOTIFY_OK :
case NOTIFY_STOP :
break ;
case NOTIFY_BAD :
die ( " Fatal Non-Maskable Interrupt " , regs , SIGINT ) ;
default :
printk ( KERN_ALERT " Got NMI, but nobody cared. Ignoring... \n " ) ;
break ;
}
nmi_exit ( ) ;
}