2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / kernel / traps . c
*
2009-10-25 18:39:37 +03:00
* Copyright ( C ) 1995 - 2009 Russell King
2005-04-17 02:20:36 +04:00
* Fragments that appear the same as linux / arch / i386 / kernel / traps . c ( C ) Linus Torvalds
*
* 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 .
*
* ' traps . c ' handles hardware exceptions after we have saved some state in
* ' linux / arch / arm / lib / traps . S ' . Mostly a debugging aid , but will probably
* kill the offending process .
*/
# include <linux/signal.h>
# include <linux/personality.h>
# include <linux/kallsyms.h>
2010-01-20 20:02:54 +03:00
# include <linux/spinlock.h>
# include <linux/uaccess.h>
2008-12-14 21:01:44 +03:00
# include <linux/hardirq.h>
2010-01-20 20:02:54 +03:00
# include <linux/kdebug.h>
2018-05-13 07:04:16 +03:00
# include <linux/kprobes.h>
2010-01-20 20:02:54 +03:00
# include <linux/module.h>
# include <linux/kexec.h>
2011-08-17 02:44:26 +04:00
# include <linux/bug.h>
2010-01-20 20:02:54 +03:00
# include <linux/delay.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
2017-02-08 20:51:30 +03:00
# include <linux/sched/signal.h>
2017-02-08 20:51:35 +03:00
# include <linux/sched/debug.h>
2017-02-08 20:51:37 +03:00
# include <linux/sched/task_stack.h>
2014-09-17 20:12:06 +04:00
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2005-04-17 02:20:36 +04:00
# include <asm/cacheflush.h>
2011-10-08 14:20:42 +04:00
# include <asm/exception.h>
2005-04-17 02:20:36 +04:00
# include <asm/unistd.h>
# include <asm/traps.h>
2014-06-03 22:50:09 +04:00
# include <asm/ptrace.h>
2009-02-16 13:41:36 +03:00
# include <asm/unwind.h>
2010-07-05 17:53:10 +04:00
# include <asm/tls.h>
2012-03-28 21:30:01 +04:00
# include <asm/system_misc.h>
2013-07-19 20:12:05 +04:00
# include <asm/opcodes.h>
2005-04-17 02:20:36 +04:00
2014-06-03 22:50:09 +04:00
2014-01-03 19:01:39 +04:00
static const char * handler [ ] = {
" prefetch abort " ,
" data abort " ,
" address exception " ,
" interrupt " ,
" undefined instruction " ,
} ;
2005-04-17 02:20:36 +04:00
2010-09-13 19:03:21 +04:00
void * vectors_page ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_DEBUG_USER
unsigned int user_debug ;
static int __init user_debug_setup ( char * str )
{
get_option ( & str , & user_debug ) ;
return 1 ;
}
__setup ( " user_debug= " , user_debug_setup ) ;
# endif
2009-10-11 18:17:53 +04:00
static void dump_mem ( const char * , const char * , unsigned long , unsigned long ) ;
2007-03-02 18:01:36 +03:00
void dump_backtrace_entry ( unsigned long where , unsigned long from , unsigned long frame )
2005-04-17 02:20:36 +04:00
{
# ifdef CONFIG_KALLSYMS
2013-10-20 18:34:10 +04:00
printk ( " [<%08lx>] (%ps) from [<%08lx>] (%pS) \n " , where , ( void * ) where , from , ( void * ) from ) ;
2005-04-17 02:20:36 +04:00
# else
printk ( " Function entered at [<%08lx>] from [<%08lx>] \n " , where , from ) ;
# endif
2007-03-02 18:01:36 +03:00
2017-11-25 02:54:22 +03:00
if ( in_entry_text ( from ) )
2009-10-11 18:17:53 +04:00
dump_mem ( " " , " Exception stack " , frame + 4 , frame + 4 + sizeof ( struct pt_regs ) ) ;
2005-04-17 02:20:36 +04:00
}
2016-11-15 16:55:59 +03:00
void dump_backtrace_stm ( u32 * stack , u32 instruction )
{
char str [ 80 ] , * p ;
unsigned int x ;
int reg ;
for ( reg = 10 , x = 0 , p = str ; reg > = 0 ; reg - - ) {
if ( instruction & BIT ( reg ) ) {
p + = sprintf ( p , " r%d:%08x " , reg , * stack - - ) ;
if ( + + x = = 6 ) {
x = 0 ;
p = str ;
printk ( " %s \n " , str ) ;
}
}
}
if ( p ! = str )
printk ( " %s \n " , str ) ;
}
2009-02-16 13:41:36 +03:00
# ifndef CONFIG_ARM_UNWIND
2005-04-17 02:20:36 +04:00
/*
* Stack pointers should always be within the kernels view of
* physical memory . If it is not there , then we can ' t dump
* out any information relating to the stack .
*/
static int verify_stack ( unsigned long sp )
{
2008-09-05 17:08:44 +04:00
if ( sp < PAGE_OFFSET | |
( sp > ( unsigned long ) high_memory & & high_memory ! = NULL ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
}
2009-02-16 13:41:36 +03:00
# endif
2005-04-17 02:20:36 +04:00
/*
* Dump out the contents of some memory nicely . . .
*/
2009-10-11 18:17:53 +04:00
static void dump_mem ( const char * lvl , const char * str , unsigned long bottom ,
unsigned long top )
2005-04-17 02:20:36 +04:00
{
2009-10-11 18:03:11 +04:00
unsigned long first ;
2005-04-17 02:20:36 +04:00
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 ) ;
2009-10-11 18:17:53 +04:00
printk ( " %s%s(0x%08lx to 0x%08lx) \n " , lvl , str , bottom , top ) ;
2005-04-17 02:20:36 +04:00
2009-10-11 18:03:11 +04:00
for ( first = bottom & ~ 31 ; first < top ; first + = 32 ) {
unsigned long p ;
char str [ sizeof ( " 12345678 " ) * 8 + 1 ] ;
2005-04-17 02:20:36 +04:00
2009-10-11 18:03:11 +04:00
memset ( str , ' ' , sizeof ( str ) ) ;
str [ sizeof ( str ) - 1 ] = ' \0 ' ;
2005-04-17 02:20:36 +04:00
2009-10-11 18:03:11 +04:00
for ( p = first , i = 0 ; i < 8 & & p < top ; i + + , p + = 4 ) {
if ( p > = bottom & & p < top ) {
unsigned long val ;
if ( __get_user ( val , ( unsigned long * ) p ) = = 0 )
sprintf ( str + i * 9 , " %08lx " , val ) ;
else
sprintf ( str + i * 9 , " ???????? " ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-10-11 18:17:53 +04:00
printk ( " %s%04lx:%s \n " , lvl , first & 0xffff , str ) ;
2005-04-17 02:20:36 +04:00
}
set_fs ( fs ) ;
}
2017-11-02 20:44:28 +03:00
static void __dump_instr ( const char * lvl , struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
unsigned long addr = instruction_pointer ( regs ) ;
const int thumb = thumb_mode ( regs ) ;
const int width = thumb ? 4 : 8 ;
2009-10-11 18:03:11 +04:00
char str [ sizeof ( " 00000000 " ) * 5 + 2 + 1 ] , * p = str ;
2005-04-17 02:20:36 +04:00
int i ;
/*
2017-11-02 20:44:28 +03:00
* Note that we now dump the code first , just in case the backtrace
* kills us .
2005-04-17 02:20:36 +04:00
*/
2011-06-10 02:21:11 +04:00
for ( i = - 4 ; i < 1 + ! ! thumb ; i + + ) {
2005-04-17 02:20:36 +04:00
unsigned int val , bad ;
if ( thumb )
2017-11-02 20:44:28 +03:00
bad = get_user ( val , & ( ( u16 * ) addr ) [ i ] ) ;
2005-04-17 02:20:36 +04:00
else
2017-11-02 20:44:28 +03:00
bad = get_user ( val , & ( ( u32 * ) addr ) [ i ] ) ;
2005-04-17 02:20:36 +04:00
if ( ! bad )
2009-10-11 18:03:11 +04:00
p + = sprintf ( p , i = = 0 ? " (%0*x) " : " %0*x " ,
width , val ) ;
2005-04-17 02:20:36 +04:00
else {
2009-10-11 18:03:11 +04:00
p + = sprintf ( p , " bad PC value " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2009-10-11 18:17:53 +04:00
printk ( " %sCode: %s \n " , lvl , str ) ;
2017-11-02 20:44:28 +03:00
}
2005-04-17 02:20:36 +04:00
2017-11-02 20:44:28 +03:00
static void dump_instr ( const char * lvl , struct pt_regs * regs )
{
mm_segment_t fs ;
if ( ! user_mode ( regs ) ) {
fs = get_fs ( ) ;
set_fs ( KERNEL_DS ) ;
__dump_instr ( lvl , regs ) ;
set_fs ( fs ) ;
} else {
__dump_instr ( lvl , regs ) ;
}
2005-04-17 02:20:36 +04:00
}
2009-02-16 13:41:36 +03:00
# ifdef CONFIG_ARM_UNWIND
static inline void dump_backtrace ( struct pt_regs * regs , struct task_struct * tsk )
{
unwind_backtrace ( regs , tsk ) ;
}
# else
2005-04-17 02:20:36 +04:00
static void dump_backtrace ( struct pt_regs * regs , struct task_struct * tsk )
{
2009-02-11 15:06:53 +03:00
unsigned int fp , mode ;
2005-04-17 02:20:36 +04:00
int ok = 1 ;
printk ( " Backtrace: " ) ;
2009-02-11 15:06:53 +03:00
if ( ! tsk )
tsk = current ;
if ( regs ) {
2014-06-03 22:50:09 +04:00
fp = frame_pointer ( regs ) ;
2009-02-11 15:06:53 +03:00
mode = processor_mode ( regs ) ;
} else if ( tsk ! = current ) {
fp = thread_saved_fp ( tsk ) ;
mode = 0x10 ;
} else {
asm ( " mov %0, fp " : " =r " ( fp ) : : " cc " ) ;
mode = 0x10 ;
}
2005-04-17 02:20:36 +04:00
if ( ! fp ) {
2014-10-28 14:26:42 +03:00
pr_cont ( " no frame pointer " ) ;
2005-04-17 02:20:36 +04:00
ok = 0 ;
} else if ( verify_stack ( fp ) ) {
2014-10-28 14:26:42 +03:00
pr_cont ( " invalid frame pointer 0x%08x " , fp ) ;
2005-04-17 02:20:36 +04:00
ok = 0 ;
2006-01-12 12:05:57 +03:00
} else if ( fp < ( unsigned long ) end_of_stack ( tsk ) )
2014-10-28 14:26:42 +03:00
pr_cont ( " frame pointer underflow " ) ;
pr_cont ( " \n " ) ;
2005-04-17 02:20:36 +04:00
if ( ok )
2009-02-11 15:06:53 +03:00
c_backtrace ( fp , mode ) ;
2005-04-17 02:20:36 +04:00
}
2009-02-16 13:41:36 +03:00
# endif
2005-04-17 02:20:36 +04:00
void show_stack ( struct task_struct * tsk , unsigned long * sp )
{
2009-02-11 15:06:53 +03:00
dump_backtrace ( NULL , tsk ) ;
2005-04-17 02:20:36 +04:00
barrier ( ) ;
}
2007-06-17 16:38:27 +04:00
# ifdef CONFIG_PREEMPT
# define S_PREEMPT " PREEMPT"
# else
# define S_PREEMPT ""
# endif
# ifdef CONFIG_SMP
# define S_SMP " SMP"
# else
# define S_SMP ""
# endif
2012-02-04 16:12:11 +04:00
# ifdef CONFIG_THUMB2_KERNEL
# define S_ISA " THUMB2"
# else
# define S_ISA " ARM"
# endif
2007-06-17 16:38:27 +04:00
2012-06-15 13:23:32 +04:00
static int __die ( const char * str , int err , struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2012-06-15 13:23:32 +04:00
struct task_struct * tsk = current ;
2005-04-17 02:20:36 +04:00
static int die_counter ;
2010-01-20 20:02:54 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
2014-10-28 14:26:42 +03:00
pr_emerg ( " Internal error: %s: %x [#%d] " S_PREEMPT S_SMP S_ISA " \n " ,
str , err , + + die_counter ) ;
2010-01-20 20:02:54 +03:00
/* trap and error numbers are mostly meaningless on ARM */
ret = notify_die ( DIE_OOPS , str , regs , err , tsk - > thread . trap_no , SIGSEGV ) ;
if ( ret = = NOTIFY_STOP )
2012-06-15 13:23:32 +04:00
return 1 ;
2010-01-20 20:02:54 +03:00
2005-04-17 02:20:36 +04:00
print_modules ( ) ;
2005-04-17 18:50:36 +04:00
__show_regs ( regs ) ;
2014-10-28 14:26:42 +03:00
pr_emerg ( " Process %.*s (pid: %d, stack limit = 0x%p) \n " ,
TASK_COMM_LEN , tsk - > comm , task_pid_nr ( tsk ) , end_of_stack ( tsk ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! user_mode ( regs ) | | in_interrupt ( ) ) {
2009-10-11 18:17:53 +04:00
dump_mem ( KERN_EMERG , " Stack: " , regs - > ARM_sp ,
2006-01-12 12:05:58 +03:00
THREAD_SIZE + ( unsigned long ) task_stack_page ( tsk ) ) ;
2005-04-17 02:20:36 +04:00
dump_backtrace ( regs , tsk ) ;
2009-10-11 18:17:53 +04:00
dump_instr ( KERN_EMERG , regs ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-20 20:02:54 +03:00
2012-06-15 13:23:32 +04:00
return 0 ;
2005-10-30 22:01:43 +03:00
}
2005-04-17 02:20:36 +04:00
2012-06-15 13:23:32 +04:00
static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED ;
static int die_owner = - 1 ;
static unsigned int die_nest_count ;
2005-10-30 22:01:43 +03:00
2012-06-15 13:23:32 +04:00
static unsigned long oops_begin ( void )
2005-10-30 22:01:43 +03:00
{
2012-06-15 13:23:32 +04:00
int cpu ;
unsigned long flags ;
2005-10-30 22:01:43 +03:00
2007-06-17 16:38:27 +04:00
oops_enter ( ) ;
2012-06-15 13:23:32 +04:00
/* racy, but better than risking deadlock. */
raw_local_irq_save ( flags ) ;
cpu = smp_processor_id ( ) ;
if ( ! arch_spin_trylock ( & die_lock ) ) {
if ( cpu = = die_owner )
/* nested oops. should stop eventually */ ;
else
arch_spin_lock ( & die_lock ) ;
}
die_nest_count + + ;
die_owner = cpu ;
2009-10-11 18:25:05 +04:00
console_verbose ( ) ;
2005-10-30 22:01:43 +03:00
bust_spinlocks ( 1 ) ;
2012-06-15 13:23:32 +04:00
return flags ;
}
2010-01-20 20:02:54 +03:00
2012-06-15 13:23:32 +04:00
static void oops_end ( unsigned long flags , struct pt_regs * regs , int signr )
{
if ( regs & & kexec_should_crash ( current ) )
2010-01-20 20:02:54 +03:00
crash_kexec ( regs ) ;
2005-04-17 02:20:36 +04:00
bust_spinlocks ( 0 ) ;
2012-06-15 13:23:32 +04:00
die_owner = - 1 ;
2013-01-21 10:47:39 +04:00
add_taint ( TAINT_DIE , LOCKDEP_NOW_UNRELIABLE ) ;
2012-06-15 13:23:32 +04:00
die_nest_count - - ;
if ( ! die_nest_count )
/* Nest count reaches zero, release the lock. */
arch_spin_unlock ( & die_lock ) ;
raw_local_irq_restore ( flags ) ;
2009-10-11 18:25:05 +04:00
oops_exit ( ) ;
2006-02-19 22:53:56 +03:00
2007-06-17 16:38:27 +04:00
if ( in_interrupt ( ) )
panic ( " Fatal exception in interrupt " ) ;
2006-07-30 14:03:34 +04:00
if ( panic_on_oops )
2006-08-14 10:24:22 +04:00
panic ( " Fatal exception " ) ;
2012-06-15 13:23:32 +04:00
if ( signr )
do_exit ( signr ) ;
}
/*
* This function is protected against re - entrancy .
*/
void die ( const char * str , struct pt_regs * regs , int err )
{
enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE ;
unsigned long flags = oops_begin ( ) ;
int sig = SIGSEGV ;
if ( ! user_mode ( regs ) )
bug_type = report_bug ( regs - > ARM_pc , regs ) ;
if ( bug_type ! = BUG_TRAP_TYPE_NONE )
str = " Oops - BUG " ;
if ( __die ( str , err , regs ) )
sig = 0 ;
oops_end ( flags , regs , sig ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-08 11:27:03 +04:00
void arm_notify_die ( const char * str , struct pt_regs * regs ,
struct siginfo * info , unsigned long err , unsigned long trap )
2005-04-17 02:20:36 +04:00
{
if ( user_mode ( regs ) ) {
current - > thread . error_code = err ;
current - > thread . trap_no = trap ;
force_sig_info ( info - > si_signo , info , current ) ;
} else {
die ( str , regs , err ) ;
}
}
2011-08-17 02:44:26 +04:00
# ifdef CONFIG_GENERIC_BUG
int is_valid_bugaddr ( unsigned long pc )
{
# ifdef CONFIG_THUMB2_KERNEL
2013-07-25 17:38:03 +04:00
u16 bkpt ;
u16 insn = __opcode_to_mem_thumb16 ( BUG_INSTR_VALUE ) ;
2011-08-17 02:44:26 +04:00
# else
2013-07-25 17:38:03 +04:00
u32 bkpt ;
u32 insn = __opcode_to_mem_arm ( BUG_INSTR_VALUE ) ;
2011-08-17 02:44:26 +04:00
# endif
if ( probe_kernel_address ( ( unsigned * ) pc , bkpt ) )
return 0 ;
2013-07-25 17:38:03 +04:00
return bkpt = = insn ;
2011-08-17 02:44:26 +04:00
}
# endif
2005-04-17 02:20:36 +04:00
static LIST_HEAD ( undef_hook ) ;
2009-07-03 17:44:46 +04:00
static DEFINE_RAW_SPINLOCK ( undef_lock ) ;
2005-04-17 02:20:36 +04:00
void register_undef_hook ( struct undef_hook * hook )
{
2005-07-16 19:43:33 +04:00
unsigned long flags ;
2009-07-03 17:44:46 +04:00
raw_spin_lock_irqsave ( & undef_lock , flags ) ;
2005-04-17 02:20:36 +04:00
list_add ( & hook - > node , & undef_hook ) ;
2009-07-03 17:44:46 +04:00
raw_spin_unlock_irqrestore ( & undef_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
void unregister_undef_hook ( struct undef_hook * hook )
{
2005-07-16 19:43:33 +04:00
unsigned long flags ;
2009-07-03 17:44:46 +04:00
raw_spin_lock_irqsave ( & undef_lock , flags ) ;
2005-04-17 02:20:36 +04:00
list_del ( & hook - > node ) ;
2009-07-03 17:44:46 +04:00
raw_spin_unlock_irqrestore ( & undef_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2018-05-13 07:04:16 +03:00
static nokprobe_inline
int call_undef_hook ( struct pt_regs * regs , unsigned int instr )
2008-08-11 15:27:16 +04:00
{
struct undef_hook * hook ;
unsigned long flags ;
int ( * fn ) ( struct pt_regs * regs , unsigned int instr ) = NULL ;
2009-07-03 17:44:46 +04:00
raw_spin_lock_irqsave ( & undef_lock , flags ) ;
2008-08-11 15:27:16 +04:00
list_for_each_entry ( hook , & undef_hook , node )
if ( ( instr & hook - > instr_mask ) = = hook - > instr_val & &
( regs - > ARM_cpsr & hook - > cpsr_mask ) = = hook - > cpsr_val )
fn = hook - > fn ;
2009-07-03 17:44:46 +04:00
raw_spin_unlock_irqrestore ( & undef_lock , flags ) ;
2008-08-11 15:27:16 +04:00
return fn ? fn ( regs , instr ) : 1 ;
}
2017-11-25 02:54:22 +03:00
asmlinkage void do_undefinstr ( struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
unsigned int instr ;
siginfo_t info ;
void __user * pc ;
2018-04-17 23:26:37 +03:00
clear_siginfo ( & info ) ;
2005-04-17 02:20:36 +04:00
pc = ( void __user * ) instruction_pointer ( regs ) ;
2007-02-13 19:11:34 +03:00
if ( processor_mode ( regs ) = = SVC_MODE ) {
2011-03-26 22:19:07 +03:00
# ifdef CONFIG_THUMB2_KERNEL
if ( thumb_mode ( regs ) ) {
2013-07-19 20:12:05 +04:00
instr = __mem_to_opcode_thumb16 ( ( ( u16 * ) pc ) [ 0 ] ) ;
2011-03-26 22:19:07 +03:00
if ( is_wide_instruction ( instr ) ) {
2013-07-19 20:12:05 +04:00
u16 inst2 ;
inst2 = __mem_to_opcode_thumb16 ( ( ( u16 * ) pc ) [ 1 ] ) ;
instr = __opcode_thumb32_compose ( instr , inst2 ) ;
2011-03-26 22:19:07 +03:00
}
} else
# endif
2013-07-19 20:12:05 +04:00
instr = __mem_to_opcode_arm ( * ( u32 * ) pc ) ;
2007-02-13 19:11:34 +03:00
} else if ( thumb_mode ( regs ) ) {
2012-09-07 21:21:44 +04:00
if ( get_user ( instr , ( u16 __user * ) pc ) )
goto die_sig ;
2013-07-19 20:12:05 +04:00
instr = __mem_to_opcode_thumb16 ( instr ) ;
2011-03-26 22:19:07 +03:00
if ( is_wide_instruction ( instr ) ) {
unsigned int instr2 ;
2012-09-07 21:21:44 +04:00
if ( get_user ( instr2 , ( u16 __user * ) pc + 1 ) )
goto die_sig ;
2013-07-19 20:12:05 +04:00
instr2 = __mem_to_opcode_thumb16 ( instr2 ) ;
instr = __opcode_thumb32_compose ( instr , instr2 ) ;
2011-03-26 22:19:07 +03:00
}
2014-01-10 04:36:00 +04:00
} else {
if ( get_user ( instr , ( u32 __user * ) pc ) )
goto die_sig ;
2013-07-19 20:12:05 +04:00
instr = __mem_to_opcode_arm ( instr ) ;
2005-04-17 02:20:36 +04:00
}
2008-08-11 15:27:16 +04:00
if ( call_undef_hook ( regs , instr ) = = 0 )
return ;
2005-04-17 02:20:36 +04:00
2012-09-07 21:21:44 +04:00
die_sig :
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_DEBUG_USER
if ( user_debug & UDBG_UNDEFINED ) {
2014-10-28 14:26:42 +03:00
pr_info ( " %s (%d): undefined instruction: pc=%p \n " ,
2007-10-19 10:40:41 +04:00
current - > comm , task_pid_nr ( current ) , pc ) ;
2014-04-10 03:49:50 +04:00
__show_regs ( regs ) ;
2009-10-11 18:17:53 +04:00
dump_instr ( KERN_INFO , regs ) ;
2005-04-17 02:20:36 +04:00
}
# endif
info . si_signo = SIGILL ;
info . si_errno = 0 ;
info . si_code = ILL_ILLOPC ;
info . si_addr = pc ;
2007-05-08 11:27:03 +04:00
arm_notify_die ( " Oops - undefined instruction " , regs , & info , 0 , 6 ) ;
2005-04-17 02:20:36 +04:00
}
2018-05-13 07:04:16 +03:00
NOKPROBE_SYMBOL ( do_undefinstr )
2005-04-17 02:20:36 +04:00
2014-09-17 20:12:06 +04:00
/*
* Handle FIQ similarly to NMI on x86 systems .
*
* The runtime environment for NMIs is extremely restrictive
* ( NMIs can pre - empt critical sections meaning almost all locking is
* forbidden ) meaning this default FIQ handling must only be used in
* circumstances where non - maskability improves robustness , such as
* watchdog or debug logic .
*
* This handler is not appropriate for general purpose use in drivers
* platform code and can be overrideen using set_fiq_handler .
*/
asmlinkage void __exception_irq_entry handle_fiq_as_nmi ( struct pt_regs * regs )
{
struct pt_regs * old_regs = set_irq_regs ( regs ) ;
nmi_enter ( ) ;
/* nop. FIQ handlers for special arch/arm features can be added here. */
nmi_exit ( ) ;
set_irq_regs ( old_regs ) ;
}
2005-04-17 02:20:36 +04:00
/*
* bad_mode handles the impossible case in the vectors . If you see one of
* these , then it ' s extremely serious , and could mean you have buggy hardware .
* It never returns , and never tries to sync . We hope that we can at least
* dump out some state information . . .
*/
2007-01-09 15:57:37 +03:00
asmlinkage void bad_mode ( struct pt_regs * regs , int reason )
2005-04-17 02:20:36 +04:00
{
console_verbose ( ) ;
2014-10-28 14:26:42 +03:00
pr_crit ( " Bad mode in %s handler detected \n " , handler [ reason ] ) ;
2005-04-17 02:20:36 +04:00
die ( " Oops - bad mode " , regs , 0 ) ;
local_irq_disable ( ) ;
panic ( " bad mode " ) ;
}
static int bad_syscall ( int n , struct pt_regs * regs )
{
siginfo_t info ;
2018-04-17 23:26:37 +03:00
clear_siginfo ( & info ) ;
2014-07-13 17:24:03 +04:00
if ( ( current - > personality & PER_MASK ) ! = PER_LINUX ) {
send_sig ( SIGSEGV , current , 1 ) ;
2005-04-17 02:20:36 +04:00
return regs - > ARM_r0 ;
}
# ifdef CONFIG_DEBUG_USER
if ( user_debug & UDBG_SYSCALL ) {
2014-10-28 14:26:42 +03:00
pr_err ( " [%d] %s: obsolete system call %08x. \n " ,
2007-10-19 10:40:41 +04:00
task_pid_nr ( current ) , current - > comm , n ) ;
2009-10-11 18:17:53 +04:00
dump_instr ( KERN_ERR , regs ) ;
2005-04-17 02:20:36 +04:00
}
# endif
info . si_signo = SIGILL ;
info . si_errno = 0 ;
info . si_code = ILL_ILLTRP ;
info . si_addr = ( void __user * ) instruction_pointer ( regs ) -
( thumb_mode ( regs ) ? 2 : 4 ) ;
2007-05-08 11:27:03 +04:00
arm_notify_die ( " Oops - bad syscall " , regs , & info , n , 0 ) ;
2005-04-17 02:20:36 +04:00
return regs - > ARM_r0 ;
}
2012-04-27 16:08:53 +04:00
static inline int
2013-05-13 18:21:49 +04:00
__do_cache_op ( unsigned long start , unsigned long end )
{
int ret ;
do {
2013-12-09 16:45:46 +04:00
unsigned long chunk = min ( PAGE_SIZE , end - start ) ;
2014-11-27 13:39:04 +03:00
if ( fatal_signal_pending ( current ) )
return 0 ;
2013-05-13 18:21:49 +04:00
ret = flush_cache_user_range ( start , start + chunk ) ;
if ( ret )
return ret ;
cond_resched ( ) ;
start + = chunk ;
} while ( start < end ) ;
return 0 ;
}
2012-04-27 16:08:53 +04:00
static inline int
2005-04-17 02:20:36 +04:00
do_cache_op ( unsigned long start , unsigned long end , int flags )
{
if ( end < start | | flags )
2012-04-27 16:08:53 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2012-08-22 14:06:54 +04:00
if ( ! access_ok ( VERIFY_READ , start , end - start ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2013-05-13 18:21:49 +04:00
return __do_cache_op ( start , end ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Handle all unrecognised system calls .
* 0x9f0000 - 0x9fffff are some more esoteric system calls
*/
# define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE)
asmlinkage int arm_syscall ( int no , struct pt_regs * regs )
{
siginfo_t info ;
2018-04-17 23:26:37 +03:00
clear_siginfo ( & info ) ;
2006-01-14 19:31:29 +03:00
if ( ( no > > 16 ) ! = ( __ARM_NR_BASE > > 16 ) )
2005-04-17 02:20:36 +04:00
return bad_syscall ( no , regs ) ;
switch ( no & 0xffff ) {
case 0 : /* branch through 0 */
info . si_signo = SIGSEGV ;
info . si_errno = 0 ;
info . si_code = SEGV_MAPERR ;
info . si_addr = NULL ;
2007-05-08 11:27:03 +04:00
arm_notify_die ( " branch through zero " , regs , & info , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
case NR ( breakpoint ) : /* SWI BREAK_POINT */
regs - > ARM_pc - = thumb_mode ( regs ) ? 2 : 4 ;
ptrace_break ( current , regs ) ;
return regs - > ARM_r0 ;
/*
* Flush a region from virtual address ' r0 ' to virtual address ' r1 '
* _exclusive_ . There is no alignment requirement on either address ;
* user space does not need to know the hardware cache layout .
*
* r2 contains flags . It should ALWAYS be passed as ZERO until it
* is defined to be something else . For now we ignore it , but may
* the fires of hell burn in your belly if you break this rule . ; )
*
* ( at a later date , we may want to allow this call to not flush
* various aspects of the cache . Passing ' 0 ' will guarantee that
* everything necessary gets flushed to maintain consistency in
* the specified region ) .
*/
case NR ( cacheflush ) :
2012-04-27 16:08:53 +04:00
return do_cache_op ( regs - > ARM_r0 , regs - > ARM_r1 , regs - > ARM_r2 ) ;
2005-04-17 02:20:36 +04:00
case NR ( usr26 ) :
if ( ! ( elf_hwcap & HWCAP_26BIT ) )
break ;
regs - > ARM_cpsr & = ~ MODE32_BIT ;
return regs - > ARM_r0 ;
case NR ( usr32 ) :
if ( ! ( elf_hwcap & HWCAP_26BIT ) )
break ;
regs - > ARM_cpsr | = MODE32_BIT ;
return regs - > ARM_r0 ;
case NR ( set_tls ) :
2014-09-11 05:49:08 +04:00
set_tls ( regs - > ARM_r0 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2017-07-28 22:35:35 +03:00
case NR ( get_tls ) :
return current_thread_info ( ) - > tp_value [ 0 ] ;
2005-04-17 02:20:36 +04:00
default :
/* Calls 9f00xx..9f07ff are defined to return -ENOSYS
if not implemented , rather than raising SIGILL . This
way the calling program can gracefully determine whether
a feature is supported . */
2009-11-08 23:05:28 +03:00
if ( ( no & 0xffff ) < = 0x7ff )
2005-04-17 02:20:36 +04:00
return - ENOSYS ;
break ;
}
# ifdef CONFIG_DEBUG_USER
/*
* experience shows that these seem to indicate that
* something catastrophic has happened
*/
if ( user_debug & UDBG_SYSCALL ) {
2014-10-28 14:26:42 +03:00
pr_err ( " [%d] %s: arm syscall %d \n " ,
2007-10-19 10:40:41 +04:00
task_pid_nr ( current ) , current - > comm , no ) ;
2009-10-11 18:17:53 +04:00
dump_instr ( " " , regs ) ;
2005-04-17 02:20:36 +04:00
if ( user_mode ( regs ) ) {
2005-04-17 18:50:36 +04:00
__show_regs ( regs ) ;
2014-06-03 22:50:09 +04:00
c_backtrace ( frame_pointer ( regs ) , processor_mode ( regs ) ) ;
2005-04-17 02:20:36 +04:00
}
}
# endif
info . si_signo = SIGILL ;
info . si_errno = 0 ;
info . si_code = ILL_ILLTRP ;
info . si_addr = ( void __user * ) instruction_pointer ( regs ) -
( thumb_mode ( regs ) ? 2 : 4 ) ;
2007-05-08 11:27:03 +04:00
arm_notify_die ( " Oops - bad syscall(2) " , regs , & info , no , 0 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-05-06 02:24:45 +04:00
# ifdef CONFIG_TLS_REG_EMUL
2005-04-30 01:08:33 +04:00
/*
* We might be running on an ARMv6 + processor which should have the TLS
2005-05-06 02:24:45 +04:00
* register but for some reason we can ' t use it , or maybe an SMP system
* using a pre - ARMv6 processor ( there are apparently a few prototypes like
* that in existence ) and therefore access to that register must be
* emulated .
2005-04-30 01:08:33 +04:00
*/
static int get_tp_trap ( struct pt_regs * regs , unsigned int instr )
{
int reg = ( instr > > 12 ) & 15 ;
if ( reg = = 15 )
return 1 ;
2013-06-19 02:23:26 +04:00
regs - > uregs [ reg ] = current_thread_info ( ) - > tp_value [ 0 ] ;
2005-04-30 01:08:33 +04:00
regs - > ARM_pc + = 4 ;
return 0 ;
}
static struct undef_hook arm_mrc_hook = {
. instr_mask = 0x0fff0fff ,
. instr_val = 0x0e1d0f70 ,
. cpsr_mask = PSR_T_BIT ,
. cpsr_val = 0 ,
. fn = get_tp_trap ,
} ;
static int __init arm_mrc_hook_init ( void )
{
register_undef_hook ( & arm_mrc_hook ) ;
return 0 ;
}
late_initcall ( arm_mrc_hook_init ) ;
# endif
2005-04-17 02:20:36 +04:00
/*
* A data abort trap was taken , but we did not handle the instruction .
* Try to abort the user program , or panic if it was the kernel .
*/
asmlinkage void
baddataabort ( int code , unsigned long instr , struct pt_regs * regs )
{
unsigned long addr = instruction_pointer ( regs ) ;
siginfo_t info ;
2018-04-17 23:26:37 +03:00
clear_siginfo ( & info ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_DEBUG_USER
if ( user_debug & UDBG_BADABORT ) {
2014-10-28 14:26:42 +03:00
pr_err ( " [%d] %s: bad data abort: code %d instr 0x%08lx \n " ,
task_pid_nr ( current ) , current - > comm , code , instr ) ;
2009-10-11 18:17:53 +04:00
dump_instr ( KERN_ERR , regs ) ;
2005-04-17 02:20:36 +04:00
show_pte ( current - > mm , addr ) ;
}
# endif
info . si_signo = SIGILL ;
info . si_errno = 0 ;
info . si_code = ILL_ILLOPC ;
info . si_addr = ( void __user * ) addr ;
2007-05-08 11:27:03 +04:00
arm_notify_die ( " unknown data abort code " , regs , & info , instr , 0 ) ;
2005-04-17 02:20:36 +04:00
}
void __readwrite_bug ( const char * fn )
{
2014-10-28 14:26:42 +03:00
pr_err ( " %s called, but not implemented \n " , fn ) ;
2005-04-17 02:20:36 +04:00
BUG ( ) ;
}
EXPORT_SYMBOL ( __readwrite_bug ) ;
2010-11-16 03:19:55 +03:00
void __pte_error ( const char * file , int line , pte_t pte )
2005-04-17 02:20:36 +04:00
{
2014-10-28 14:26:42 +03:00
pr_err ( " %s:%d: bad pte %08llx. \n " , file , line , ( long long ) pte_val ( pte ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-11-16 03:19:55 +03:00
void __pmd_error ( const char * file , int line , pmd_t pmd )
2005-04-17 02:20:36 +04:00
{
2014-10-28 14:26:42 +03:00
pr_err ( " %s:%d: bad pmd %08llx. \n " , file , line , ( long long ) pmd_val ( pmd ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-11-16 03:19:55 +03:00
void __pgd_error ( const char * file , int line , pgd_t pgd )
2005-04-17 02:20:36 +04:00
{
2014-10-28 14:26:42 +03:00
pr_err ( " %s:%d: bad pgd %08llx. \n " , file , line , ( long long ) pgd_val ( pgd ) ) ;
2005-04-17 02:20:36 +04:00
}
asmlinkage void __div0 ( void )
{
2014-10-28 14:26:42 +03:00
pr_err ( " Division by zero in kernel. \n " ) ;
2005-04-17 02:20:36 +04:00
dump_stack ( ) ;
}
EXPORT_SYMBOL ( __div0 ) ;
void abort ( void )
{
BUG ( ) ;
/* if that doesn't kill us, halt */
panic ( " Oops failed to kill thread " ) ;
}
void __init trap_init ( void )
2008-02-20 22:33:40 +03:00
{
return ;
}
2013-07-23 21:37:00 +04:00
# ifdef CONFIG_KUSER_HELPERS
static void __init kuser_init ( void * vectors )
2010-07-05 17:53:10 +04:00
{
2013-07-23 21:37:00 +04:00
extern char __kuser_helper_start [ ] , __kuser_helper_end [ ] ;
int kuser_sz = __kuser_helper_end - __kuser_helper_start ;
memcpy ( vectors + 0x1000 - kuser_sz , __kuser_helper_start , kuser_sz ) ;
2010-07-05 17:53:10 +04:00
/*
* vectors + 0xfe0 = __kuser_get_tls
* vectors + 0xfe8 = hardware TLS instruction at 0xffff0fe8
*/
if ( tls_emu | | has_tls_reg )
2013-07-23 21:37:00 +04:00
memcpy ( vectors + 0xfe0 , vectors + 0xfe8 , 4 ) ;
}
# else
2013-11-18 09:01:38 +04:00
static inline void __init kuser_init ( void * vectors )
2013-07-23 21:37:00 +04:00
{
2010-07-05 17:53:10 +04:00
}
2013-07-23 21:37:00 +04:00
# endif
2010-07-05 17:53:10 +04:00
2012-01-18 19:32:49 +04:00
void __init early_trap_init ( void * vectors_base )
2005-04-17 02:20:36 +04:00
{
2010-05-21 21:06:41 +04:00
# ifndef CONFIG_CPU_V7M
2012-01-18 19:32:49 +04:00
unsigned long vectors = ( unsigned long ) vectors_base ;
2005-04-26 18:17:42 +04:00
extern char __stubs_start [ ] , __stubs_end [ ] ;
extern char __vectors_start [ ] , __vectors_end [ ] ;
2013-07-04 14:00:23 +04:00
unsigned i ;
2005-04-17 02:20:36 +04:00
2012-01-18 19:32:49 +04:00
vectors_page = vectors_base ;
2013-07-04 14:00:23 +04:00
/*
* Poison the vectors page with an undefined instruction . This
* instruction is chosen to be undefined for both ARM and Thumb
* ISAs . The Thumb version is an undefined instruction with a
* branch back to the undefined instruction .
*/
for ( i = 0 ; i < PAGE_SIZE / sizeof ( u32 ) ; i + + )
( ( u32 * ) vectors_base ) [ i ] = 0xe7fddef1 ;
2005-04-26 18:17:42 +04:00
/*
2005-04-30 01:08:33 +04:00
* Copy the vectors , stubs and kuser helpers ( in entry - armv . S )
* into the vector page , mapped at 0xffff0000 , and ensure these
* are visible to the instruction stream .
2005-04-26 18:17:42 +04:00
*/
2006-03-27 18:18:50 +04:00
memcpy ( ( void * ) vectors , __vectors_start , __vectors_end - __vectors_start ) ;
2013-07-04 14:40:32 +04:00
memcpy ( ( void * ) vectors + 0x1000 , __stubs_start , __stubs_end - __stubs_start ) ;
2005-06-22 23:26:05 +04:00
2013-07-23 21:37:00 +04:00
kuser_init ( vectors_base ) ;
2010-07-05 17:53:10 +04:00
2013-07-04 14:40:32 +04:00
flush_icache_range ( vectors , vectors + PAGE_SIZE * 2 ) ;
2010-05-21 21:06:41 +04:00
# else /* ifndef CONFIG_CPU_V7M */
/*
* on V7 - M there is no need to copy the vector table to a dedicated
* memory area . The address is configurable and so a table in the kernel
* image can be used .
*/
# endif
2005-04-17 02:20:36 +04:00
}