2012-03-05 15:49:27 +04:00
/*
* Based on arch / arm / kernel / traps . c
*
* Copyright ( C ) 1995 - 2009 Russell King
* Copyright ( C ) 2012 ARM Ltd .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2015-07-24 18:37:48 +03:00
# include <linux/bug.h>
2012-03-05 15:49:27 +04:00
# include <linux/signal.h>
# include <linux/personality.h>
# include <linux/kallsyms.h>
# include <linux/spinlock.h>
# include <linux/uaccess.h>
# include <linux/hardirq.h>
# include <linux/kdebug.h>
# include <linux/module.h>
# include <linux/kexec.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/syscalls.h>
# include <asm/atomic.h>
2015-07-24 18:37:48 +03:00
# include <asm/bug.h>
2013-03-16 12:48:13 +04:00
# include <asm/debug-monitors.h>
2014-11-18 15:16:30 +03:00
# include <asm/esr.h>
2015-07-24 18:37:48 +03:00
# include <asm/insn.h>
2012-03-05 15:49:27 +04:00
# include <asm/traps.h>
# include <asm/stacktrace.h>
# include <asm/exception.h>
# include <asm/system_misc.h>
static const char * handler [ ] = {
" Synchronous Abort " ,
" IRQ " ,
" FIQ " ,
" Error "
} ;
int show_unhandled_signals = 1 ;
/*
* Dump out the contents of some memory nicely . . .
*/
static void dump_mem ( const char * lvl , const char * str , unsigned long bottom ,
2015-07-10 11:23:59 +03:00
unsigned long top , bool compat )
2012-03-05 15:49:27 +04:00
{
unsigned long first ;
mm_segment_t fs ;
int i ;
2015-07-10 11:23:59 +03:00
unsigned int width = compat ? 4 : 8 ;
2012-03-05 15:49:27 +04:00
/*
* We need to switch to kernel mode so that we can use __get_user
* to safely read from kernel space . Note that we now dump the
* code first , just in case the backtrace kills us .
*/
fs = get_fs ( ) ;
set_fs ( KERNEL_DS ) ;
printk ( " %s%s(0x%016lx to 0x%016lx) \n " , lvl , str , bottom , top ) ;
for ( first = bottom & ~ 31 ; first < top ; first + = 32 ) {
unsigned long p ;
char str [ sizeof ( " 12345678 " ) * 8 + 1 ] ;
memset ( str , ' ' , sizeof ( str ) ) ;
str [ sizeof ( str ) - 1 ] = ' \0 ' ;
2015-07-10 11:23:59 +03:00
for ( p = first , i = 0 ; i < ( 32 / width )
& & p < top ; i + + , p + = width ) {
2012-03-05 15:49:27 +04:00
if ( p > = bottom & & p < top ) {
2015-07-10 11:23:59 +03:00
unsigned long val ;
if ( width = = 8 ) {
if ( __get_user ( val , ( unsigned long * ) p ) = = 0 )
sprintf ( str + i * 17 , " %016lx " , val ) ;
else
sprintf ( str + i * 17 , " ???????????????? " ) ;
} else {
if ( __get_user ( val , ( unsigned int * ) p ) = = 0 )
sprintf ( str + i * 9 , " %08lx " , val ) ;
else
sprintf ( str + i * 9 , " ???????? " ) ;
}
2012-03-05 15:49:27 +04:00
}
}
printk ( " %s%04lx:%s \n " , lvl , first & 0xffff , str ) ;
}
set_fs ( fs ) ;
}
2015-10-17 17:28:11 +03:00
static void dump_backtrace_entry ( unsigned long where )
2012-03-05 15:49:27 +04:00
{
2015-10-17 17:28:11 +03:00
/*
* Note that ' where ' can have a physical address , but it ' s not handled .
*/
2012-03-05 15:49:27 +04:00
print_ip_sym ( where ) ;
}
static void dump_instr ( const char * lvl , struct pt_regs * regs )
{
unsigned long addr = instruction_pointer ( regs ) ;
mm_segment_t fs ;
char str [ sizeof ( " 00000000 " ) * 5 + 2 + 1 ] , * p = str ;
int i ;
/*
* We need to switch to kernel mode so that we can use __get_user
* to safely read from kernel space . Note that we now dump the
* code first , just in case the backtrace kills us .
*/
fs = get_fs ( ) ;
set_fs ( KERNEL_DS ) ;
for ( i = - 4 ; i < 1 ; i + + ) {
unsigned int val , bad ;
bad = __get_user ( val , & ( ( u32 * ) addr ) [ i ] ) ;
if ( ! bad )
p + = sprintf ( p , i = = 0 ? " (%08x) " : " %08x " , val ) ;
else {
p + = sprintf ( p , " bad PC value " ) ;
break ;
}
}
printk ( " %sCode: %s \n " , lvl , str ) ;
set_fs ( fs ) ;
}
static void dump_backtrace ( struct pt_regs * regs , struct task_struct * tsk )
{
struct stackframe frame ;
2016-02-12 00:53:10 +03:00
unsigned long irq_stack_ptr ;
2015-12-15 11:33:41 +03:00
int skip ;
2012-03-05 15:49:27 +04:00
2016-02-12 00:53:10 +03:00
/*
* Switching between stacks is valid when tracing current and in
* non - preemptible context .
*/
if ( tsk = = current & & ! preemptible ( ) )
irq_stack_ptr = IRQ_STACK_PTR ( smp_processor_id ( ) ) ;
else
irq_stack_ptr = 0 ;
2012-03-05 15:49:27 +04:00
pr_debug ( " %s(regs = %p tsk = %p) \n " , __func__ , regs , tsk ) ;
if ( ! tsk )
tsk = current ;
2015-12-15 11:33:41 +03:00
if ( tsk = = current ) {
2012-03-05 15:49:27 +04:00
frame . fp = ( unsigned long ) __builtin_frame_address ( 0 ) ;
2014-08-27 08:29:32 +04:00
frame . sp = current_stack_pointer ;
2012-03-05 15:49:27 +04:00
frame . pc = ( unsigned long ) dump_backtrace ;
} else {
/*
* task blocked in __switch_to
*/
frame . fp = thread_saved_fp ( tsk ) ;
frame . sp = thread_saved_sp ( tsk ) ;
frame . pc = thread_saved_pc ( tsk ) ;
}
2015-12-15 11:33:41 +03:00
# ifdef CONFIG_FUNCTION_GRAPH_TRACER
frame . graph = tsk - > curr_ret_stack ;
# endif
2012-03-05 15:49:27 +04:00
2015-12-15 11:33:41 +03:00
skip = ! ! regs ;
2015-12-21 19:44:27 +03:00
printk ( " Call trace: \n " ) ;
2012-03-05 15:49:27 +04:00
while ( 1 ) {
unsigned long where = frame . pc ;
2015-10-17 17:28:11 +03:00
unsigned long stack ;
2012-03-05 15:49:27 +04:00
int ret ;
2015-12-15 11:33:41 +03:00
/* skip until specified stack frame */
if ( ! skip ) {
dump_backtrace_entry ( where ) ;
} else if ( frame . fp = = regs - > regs [ 29 ] ) {
skip = 0 ;
/*
* Mostly , this is the case where this function is
* called in panic / abort . As exception handler ' s
* stack frame does not contain the corresponding pc
* at which an exception has taken place , use regs - > pc
* instead .
*/
dump_backtrace_entry ( regs - > pc ) ;
}
2015-12-15 11:33:40 +03:00
ret = unwind_frame ( tsk , & frame ) ;
2012-03-05 15:49:27 +04:00
if ( ret < 0 )
break ;
2015-10-17 17:28:11 +03:00
stack = frame . sp ;
2015-12-04 14:02:26 +03:00
if ( in_exception_text ( where ) ) {
/*
* If we switched to the irq_stack before calling this
* exception handler , then the pt_regs will be on the
* task stack . The easiest way to tell is if the large
* pt_regs would overlap with the end of the irq_stack .
*/
if ( stack < irq_stack_ptr & &
( stack + sizeof ( struct pt_regs ) ) > irq_stack_ptr )
stack = IRQ_STACK_TO_TASK_STACK ( irq_stack_ptr ) ;
2015-10-17 17:28:11 +03:00
dump_mem ( " " , " Exception stack " , stack ,
stack + sizeof ( struct pt_regs ) , false ) ;
2015-12-04 14:02:26 +03:00
}
2012-03-05 15:49:27 +04:00
}
}
void show_stack ( struct task_struct * tsk , unsigned long * sp )
{
dump_backtrace ( NULL , tsk ) ;
barrier ( ) ;
}
# ifdef CONFIG_PREEMPT
# define S_PREEMPT " PREEMPT"
# else
# define S_PREEMPT ""
# endif
# define S_SMP " SMP"
static int __die ( const char * str , int err , struct thread_info * thread ,
struct pt_regs * regs )
{
struct task_struct * tsk = thread - > task ;
static int die_counter ;
int ret ;
pr_emerg ( " Internal error: %s: %x [#%d] " S_PREEMPT S_SMP " \n " ,
str , err , + + die_counter ) ;
/* trap and error numbers are mostly meaningless on ARM */
ret = notify_die ( DIE_OOPS , str , regs , err , 0 , SIGSEGV ) ;
if ( ret = = NOTIFY_STOP )
return ret ;
print_modules ( ) ;
__show_regs ( regs ) ;
pr_emerg ( " Process %.*s (pid: %d, stack limit = 0x%p) \n " ,
TASK_COMM_LEN , tsk - > comm , task_pid_nr ( tsk ) , thread + 1 ) ;
if ( ! user_mode ( regs ) | | in_interrupt ( ) ) {
dump_mem ( KERN_EMERG , " Stack: " , regs - > sp ,
2015-07-10 11:23:59 +03:00
THREAD_SIZE + ( unsigned long ) task_stack_page ( tsk ) ,
compat_user_mode ( regs ) ) ;
2012-03-05 15:49:27 +04:00
dump_backtrace ( regs , tsk ) ;
dump_instr ( KERN_EMERG , regs ) ;
}
return ret ;
}
static DEFINE_RAW_SPINLOCK ( die_lock ) ;
/*
* This function is protected against re - entrancy .
*/
void die ( const char * str , struct pt_regs * regs , int err )
{
struct thread_info * thread = current_thread_info ( ) ;
int ret ;
oops_enter ( ) ;
raw_spin_lock_irq ( & die_lock ) ;
console_verbose ( ) ;
bust_spinlocks ( 1 ) ;
ret = __die ( str , err , thread , regs ) ;
if ( regs & & kexec_should_crash ( thread - > task ) )
crash_kexec ( regs ) ;
bust_spinlocks ( 0 ) ;
2013-01-21 10:47:39 +04:00
add_taint ( TAINT_DIE , LOCKDEP_NOW_UNRELIABLE ) ;
2012-03-05 15:49:27 +04:00
raw_spin_unlock_irq ( & die_lock ) ;
oops_exit ( ) ;
if ( in_interrupt ( ) )
panic ( " Fatal exception in interrupt " ) ;
if ( panic_on_oops )
panic ( " Fatal exception " ) ;
if ( ret ! = NOTIFY_STOP )
do_exit ( SIGSEGV ) ;
}
void arm64_notify_die ( const char * str , struct pt_regs * regs ,
struct siginfo * info , int err )
{
2014-04-07 02:04:12 +04:00
if ( user_mode ( regs ) ) {
current - > thread . fault_address = 0 ;
current - > thread . fault_code = err ;
2012-03-05 15:49:27 +04:00
force_sig_info ( info - > si_signo , info , current ) ;
2014-04-07 02:04:12 +04:00
} else {
2012-03-05 15:49:27 +04:00
die ( str , regs , err ) ;
2014-04-07 02:04:12 +04:00
}
2012-03-05 15:49:27 +04:00
}
2014-11-18 14:41:22 +03:00
static LIST_HEAD ( undef_hook ) ;
static DEFINE_RAW_SPINLOCK ( undef_lock ) ;
void register_undef_hook ( struct undef_hook * hook )
{
unsigned long flags ;
raw_spin_lock_irqsave ( & undef_lock , flags ) ;
list_add ( & hook - > node , & undef_hook ) ;
raw_spin_unlock_irqrestore ( & undef_lock , flags ) ;
}
void unregister_undef_hook ( struct undef_hook * hook )
{
unsigned long flags ;
raw_spin_lock_irqsave ( & undef_lock , flags ) ;
list_del ( & hook - > node ) ;
raw_spin_unlock_irqrestore ( & undef_lock , flags ) ;
}
static int call_undef_hook ( struct pt_regs * regs )
{
struct undef_hook * hook ;
unsigned long flags ;
u32 instr ;
int ( * fn ) ( struct pt_regs * regs , u32 instr ) = NULL ;
void __user * pc = ( void __user * ) instruction_pointer ( regs ) ;
if ( ! user_mode ( regs ) )
return 1 ;
if ( compat_thumb_mode ( regs ) ) {
/* 16-bit Thumb instruction */
if ( get_user ( instr , ( u16 __user * ) pc ) )
goto exit ;
instr = le16_to_cpu ( instr ) ;
if ( aarch32_insn_is_wide ( instr ) ) {
u32 instr2 ;
if ( get_user ( instr2 , ( u16 __user * ) ( pc + 2 ) ) )
goto exit ;
instr2 = le16_to_cpu ( instr2 ) ;
instr = ( instr < < 16 ) | instr2 ;
}
} else {
/* 32-bit ARM instruction */
if ( get_user ( instr , ( u32 __user * ) pc ) )
goto exit ;
instr = le32_to_cpu ( instr ) ;
}
raw_spin_lock_irqsave ( & undef_lock , flags ) ;
list_for_each_entry ( hook , & undef_hook , node )
if ( ( instr & hook - > instr_mask ) = = hook - > instr_val & &
( regs - > pstate & hook - > pstate_mask ) = = hook - > pstate_val )
fn = hook - > fn ;
raw_spin_unlock_irqrestore ( & undef_lock , flags ) ;
exit :
return fn ? fn ( regs , instr ) : 1 ;
}
2012-03-05 15:49:27 +04:00
asmlinkage void __exception do_undefinstr ( struct pt_regs * regs )
{
siginfo_t info ;
void __user * pc = ( void __user * ) instruction_pointer ( regs ) ;
/* check for AArch32 breakpoint instructions */
2013-03-16 12:48:13 +04:00
if ( ! aarch32_break_handler ( regs ) )
2012-03-05 15:49:27 +04:00
return ;
2014-11-18 14:41:22 +03:00
if ( call_undef_hook ( regs ) = = 0 )
return ;
2015-07-03 17:08:08 +03:00
if ( unhandled_signal ( current , SIGILL ) & & show_unhandled_signals_ratelimited ( ) ) {
2012-03-05 15:49:27 +04:00
pr_info ( " %s[%d]: undefined instruction: pc=%p \n " ,
current - > comm , task_pid_nr ( current ) , pc ) ;
dump_instr ( KERN_INFO , regs ) ;
}
info . si_signo = SIGILL ;
info . si_errno = 0 ;
info . si_code = ILL_ILLOPC ;
info . si_addr = pc ;
arm64_notify_die ( " Oops - undefined instruction " , regs , & info , 0 ) ;
}
long compat_arm_syscall ( struct pt_regs * regs ) ;
asmlinkage long do_ni_syscall ( struct pt_regs * regs )
{
# ifdef CONFIG_COMPAT
long ret ;
if ( is_compat_task ( ) ) {
ret = compat_arm_syscall ( regs ) ;
if ( ret ! = - ENOSYS )
return ret ;
}
# endif
2015-06-19 17:28:03 +03:00
if ( show_unhandled_signals_ratelimited ( ) ) {
2012-03-05 15:49:27 +04:00
pr_info ( " %s[%d]: syscall %d \n " , current - > comm ,
task_pid_nr ( current ) , ( int ) regs - > syscallno ) ;
dump_instr ( " " , regs ) ;
if ( user_mode ( regs ) )
__show_regs ( regs ) ;
}
return sys_ni_syscall ( ) ;
}
2014-11-18 15:16:30 +03:00
static const char * esr_class_str [ ] = {
[ 0 . . . ESR_ELx_EC_MAX ] = " UNRECOGNIZED EC " ,
[ ESR_ELx_EC_UNKNOWN ] = " Unknown/Uncategorized " ,
[ ESR_ELx_EC_WFx ] = " WFI/WFE " ,
[ ESR_ELx_EC_CP15_32 ] = " CP15 MCR/MRC " ,
[ ESR_ELx_EC_CP15_64 ] = " CP15 MCRR/MRRC " ,
[ ESR_ELx_EC_CP14_MR ] = " CP14 MCR/MRC " ,
[ ESR_ELx_EC_CP14_LS ] = " CP14 LDC/STC " ,
[ ESR_ELx_EC_FP_ASIMD ] = " ASIMD " ,
[ ESR_ELx_EC_CP10_ID ] = " CP10 MRC/VMRS " ,
[ ESR_ELx_EC_CP14_64 ] = " CP14 MCRR/MRRC " ,
[ ESR_ELx_EC_ILL ] = " PSTATE.IL " ,
[ ESR_ELx_EC_SVC32 ] = " SVC (AArch32) " ,
[ ESR_ELx_EC_HVC32 ] = " HVC (AArch32) " ,
[ ESR_ELx_EC_SMC32 ] = " SMC (AArch32) " ,
[ ESR_ELx_EC_SVC64 ] = " SVC (AArch64) " ,
[ ESR_ELx_EC_HVC64 ] = " HVC (AArch64) " ,
[ ESR_ELx_EC_SMC64 ] = " SMC (AArch64) " ,
[ ESR_ELx_EC_SYS64 ] = " MSR/MRS (AArch64) " ,
[ ESR_ELx_EC_IMP_DEF ] = " EL3 IMP DEF " ,
[ ESR_ELx_EC_IABT_LOW ] = " IABT (lower EL) " ,
[ ESR_ELx_EC_IABT_CUR ] = " IABT (current EL) " ,
[ ESR_ELx_EC_PC_ALIGN ] = " PC Alignment " ,
[ ESR_ELx_EC_DABT_LOW ] = " DABT (lower EL) " ,
[ ESR_ELx_EC_DABT_CUR ] = " DABT (current EL) " ,
[ ESR_ELx_EC_SP_ALIGN ] = " SP Alignment " ,
[ ESR_ELx_EC_FP_EXC32 ] = " FP (AArch32) " ,
[ ESR_ELx_EC_FP_EXC64 ] = " FP (AArch64) " ,
[ ESR_ELx_EC_SERROR ] = " SError " ,
[ ESR_ELx_EC_BREAKPT_LOW ] = " Breakpoint (lower EL) " ,
[ ESR_ELx_EC_BREAKPT_CUR ] = " Breakpoint (current EL) " ,
[ ESR_ELx_EC_SOFTSTP_LOW ] = " Software Step (lower EL) " ,
[ ESR_ELx_EC_SOFTSTP_CUR ] = " Software Step (current EL) " ,
[ ESR_ELx_EC_WATCHPT_LOW ] = " Watchpoint (lower EL) " ,
[ ESR_ELx_EC_WATCHPT_CUR ] = " Watchpoint (current EL) " ,
[ ESR_ELx_EC_BKPT32 ] = " BKPT (AArch32) " ,
[ ESR_ELx_EC_VECTOR32 ] = " Vector catch (AArch32) " ,
[ ESR_ELx_EC_BRK64 ] = " BRK (AArch64) " ,
} ;
const char * esr_get_class_string ( u32 esr )
{
return esr_class_str [ esr > > ESR_ELx_EC_SHIFT ] ;
}
2012-03-05 15:49:27 +04:00
/*
* bad_mode handles the impossible case in the exception vector .
*/
asmlinkage void bad_mode ( struct pt_regs * regs , int reason , unsigned int esr )
{
2013-05-28 18:54:15 +04:00
siginfo_t info ;
void __user * pc = ( void __user * ) instruction_pointer ( regs ) ;
2012-03-05 15:49:27 +04:00
console_verbose ( ) ;
2014-11-18 15:16:30 +03:00
pr_crit ( " Bad mode in %s handler detected, code 0x%08x -- %s \n " ,
handler [ reason ] , esr , esr_get_class_string ( esr ) ) ;
2013-05-28 18:54:15 +04:00
__show_regs ( regs ) ;
info . si_signo = SIGILL ;
info . si_errno = 0 ;
info . si_code = ILL_ILLOPC ;
info . si_addr = pc ;
2012-03-05 15:49:27 +04:00
2013-05-28 18:54:15 +04:00
arm64_notify_die ( " Oops - bad mode " , regs , & info , 0 ) ;
2012-03-05 15:49:27 +04:00
}
void __pte_error ( const char * file , int line , unsigned long val )
{
2015-12-21 19:44:27 +03:00
pr_err ( " %s:%d: bad pte %016lx. \n " , file , line , val ) ;
2012-03-05 15:49:27 +04:00
}
void __pmd_error ( const char * file , int line , unsigned long val )
{
2015-12-21 19:44:27 +03:00
pr_err ( " %s:%d: bad pmd %016lx. \n " , file , line , val ) ;
2012-03-05 15:49:27 +04:00
}
2014-05-12 13:40:51 +04:00
void __pud_error ( const char * file , int line , unsigned long val )
{
2015-12-21 19:44:27 +03:00
pr_err ( " %s:%d: bad pud %016lx. \n " , file , line , val ) ;
2014-05-12 13:40:51 +04:00
}
2012-03-05 15:49:27 +04:00
void __pgd_error ( const char * file , int line , unsigned long val )
{
2015-12-21 19:44:27 +03:00
pr_err ( " %s:%d: bad pgd %016lx. \n " , file , line , val ) ;
2012-03-05 15:49:27 +04:00
}
2015-07-24 18:37:48 +03:00
/* GENERIC_BUG traps */
int is_valid_bugaddr ( unsigned long addr )
{
/*
* bug_handler ( ) only called for BRK # BUG_BRK_IMM .
* So the answer is trivial - - any spurious instances with no
* bug table entry will be rejected by report_bug ( ) and passed
* back to the debug - monitors code and handled as a fatal
* unexpected debug exception .
*/
return 1 ;
}
static int bug_handler ( struct pt_regs * regs , unsigned int esr )
{
if ( user_mode ( regs ) )
return DBG_HOOK_ERROR ;
switch ( report_bug ( regs - > pc , regs ) ) {
case BUG_TRAP_TYPE_BUG :
die ( " Oops - BUG " , regs , 0 ) ;
break ;
case BUG_TRAP_TYPE_WARN :
2015-07-24 18:37:49 +03:00
/* Ideally, report_bug() should backtrace for us... but no. */
dump_backtrace ( regs , NULL ) ;
2015-07-24 18:37:48 +03:00
break ;
default :
/* unknown/unrecognised bug trap type */
return DBG_HOOK_ERROR ;
}
/* If thread survives, skip over the BUG instruction and continue: */
regs - > pc + = AARCH64_INSN_SIZE ; /* skip BRK and resume */
return DBG_HOOK_HANDLED ;
}
static struct break_hook bug_break_hook = {
. esr_val = 0xf2000000 | BUG_BRK_IMM ,
. esr_mask = 0xffffffff ,
. fn = bug_handler ,
} ;
/*
* Initial handler for AArch64 BRK exceptions
* This handler only used until debug_traps_init ( ) .
*/
int __init early_brk64 ( unsigned long addr , unsigned int esr ,
struct pt_regs * regs )
{
return bug_handler ( regs , esr ) ! = DBG_HOOK_HANDLED ;
}
/* This registration must happen early, before debug_traps_init(). */
2012-03-05 15:49:27 +04:00
void __init trap_init ( void )
{
2015-07-24 18:37:48 +03:00
register_break_hook ( & bug_break_hook ) ;
2012-03-05 15:49:27 +04:00
}