2012-03-05 11:49:27 +00: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/>.
*/
# 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>
2013-03-16 08:48:13 +00:00
# include <asm/debug-monitors.h>
2012-03-05 11:49:27 +00: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 ,
unsigned long top )
{
unsigned long first ;
mm_segment_t fs ;
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 ) ;
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 ' ;
for ( p = first , i = 0 ; i < 8 & & p < top ; i + + , p + = 4 ) {
if ( p > = bottom & & p < top ) {
unsigned int val ;
if ( __get_user ( val , ( unsigned int * ) p ) = = 0 )
sprintf ( str + i * 9 , " %08x " , val ) ;
else
sprintf ( str + i * 9 , " ???????? " ) ;
}
}
printk ( " %s%04lx:%s \n " , lvl , first & 0xffff , str ) ;
}
set_fs ( fs ) ;
}
static void dump_backtrace_entry ( unsigned long where , unsigned long stack )
{
print_ip_sym ( where ) ;
if ( in_exception_text ( where ) )
dump_mem ( " " , " Exception stack " , stack ,
stack + sizeof ( struct pt_regs ) ) ;
}
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 ;
const register unsigned long current_sp asm ( " sp " ) ;
pr_debug ( " %s(regs = %p tsk = %p) \n " , __func__ , regs , tsk ) ;
if ( ! tsk )
tsk = current ;
if ( regs ) {
frame . fp = regs - > regs [ 29 ] ;
frame . sp = regs - > sp ;
frame . pc = regs - > pc ;
} else if ( tsk = = current ) {
frame . fp = ( unsigned long ) __builtin_frame_address ( 0 ) ;
frame . sp = current_sp ;
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 ) ;
}
printk ( " Call trace: \n " ) ;
while ( 1 ) {
unsigned long where = frame . pc ;
int ret ;
ret = unwind_frame ( & frame ) ;
if ( ret < 0 )
break ;
dump_backtrace_entry ( where , frame . sp ) ;
}
}
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
# ifdef CONFIG_SMP
# define S_SMP " SMP"
# else
# define S_SMP ""
# endif
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 ,
THREAD_SIZE + ( unsigned long ) task_stack_page ( tsk ) ) ;
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 17:17:39 +10:30
add_taint ( TAINT_DIE , LOCKDEP_NOW_UNRELIABLE ) ;
2012-03-05 11:49:27 +00: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 )
{
if ( user_mode ( regs ) )
force_sig_info ( info - > si_signo , info , current ) ;
else
die ( str , regs , err ) ;
}
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 08:48:13 +00:00
if ( ! aarch32_break_handler ( regs ) )
2012-03-05 11:49:27 +00:00
return ;
2013-05-21 12:16:56 +01:00
if ( show_unhandled_signals & & unhandled_signal ( current , SIGILL ) & &
printk_ratelimit ( ) ) {
2012-03-05 11:49:27 +00: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
2013-05-21 12:16:56 +01:00
if ( show_unhandled_signals & & printk_ratelimit ( ) ) {
2012-03-05 11:49:27 +00: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 ( ) ;
}
/*
* 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 15:54:15 +01:00
siginfo_t info ;
void __user * pc = ( void __user * ) instruction_pointer ( regs ) ;
2012-03-05 11:49:27 +00:00
console_verbose ( ) ;
pr_crit ( " Bad mode in %s handler detected, code 0x%08x \n " ,
handler [ reason ] , esr ) ;
2013-05-28 15:54:15 +01:00
__show_regs ( regs ) ;
info . si_signo = SIGILL ;
info . si_errno = 0 ;
info . si_code = ILL_ILLOPC ;
info . si_addr = pc ;
2012-03-05 11:49:27 +00:00
2013-05-28 15:54:15 +01:00
arm64_notify_die ( " Oops - bad mode " , regs , & info , 0 ) ;
2012-03-05 11:49:27 +00:00
}
void __pte_error ( const char * file , int line , unsigned long val )
{
printk ( " %s:%d: bad pte %016lx. \n " , file , line , val ) ;
}
void __pmd_error ( const char * file , int line , unsigned long val )
{
printk ( " %s:%d: bad pmd %016lx. \n " , file , line , val ) ;
}
void __pgd_error ( const char * file , int line , unsigned long val )
{
printk ( " %s:%d: bad pgd %016lx. \n " , file , line , val ) ;
}
void __init trap_init ( void )
{
return ;
}