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>
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
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 ( ) ;
}