2014-11-06 10:19:40 +03:00
/*
* Hardware exception handling
*
* Copyright ( C ) 2010 Tobias Klauser < tklauser @ distanz . ch >
* Copyright ( C ) 2004 Microtronix Datacom Ltd .
* Copyright ( C ) 2001 Vic Phillips
*
* This file is subject to the terms and conditions of the GNU General
* Public License . See the file COPYING in the main directory of this
* archive for more details .
*/
# include <linux/sched.h>
2017-02-08 20:51:35 +03:00
# include <linux/sched/debug.h>
2014-11-06 10:19:40 +03:00
# include <linux/kernel.h>
# include <linux/signal.h>
# include <linux/export.h>
# include <linux/mm.h>
# include <linux/ptrace.h>
# include <asm/traps.h>
# include <asm/sections.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2014-11-06 10:19:40 +03:00
static DEFINE_SPINLOCK ( die_lock ) ;
2015-04-16 10:19:01 +03:00
static void _send_sig ( int signo , int code , unsigned long addr )
{
2019-05-23 19:04:24 +03:00
force_sig_fault ( signo , code , ( void __user * ) addr ) ;
2015-04-16 10:19:01 +03:00
}
2014-11-06 10:19:40 +03:00
void die ( const char * str , struct pt_regs * regs , long err )
{
console_verbose ( ) ;
spin_lock_irq ( & die_lock ) ;
pr_warn ( " Oops: %s, sig: %ld \n " , str , err ) ;
show_regs ( regs ) ;
spin_unlock_irq ( & die_lock ) ;
/*
* do_exit ( ) should take care of panic ' ing from an interrupt
* context so we don ' t handle it here
*/
do_exit ( err ) ;
}
void _exception ( int signo , struct pt_regs * regs , int code , unsigned long addr )
{
if ( ! user_mode ( regs ) )
die ( " Exception in kernel mode " , regs , signo ) ;
2015-04-16 10:19:01 +03:00
_send_sig ( signo , code , addr ) ;
2014-11-06 10:19:40 +03:00
}
/*
2020-06-09 07:32:29 +03:00
* The show_stack ( ) is external API which we do not use ourselves .
2014-11-06 10:19:40 +03:00
*/
int kstack_depth_to_print = 48 ;
2020-06-09 07:32:29 +03:00
void show_stack ( struct task_struct * task , unsigned long * stack ,
const char * loglvl )
2014-11-06 10:19:40 +03:00
{
unsigned long * endstack , addr ;
int i ;
if ( ! stack ) {
if ( task )
stack = ( unsigned long * ) task - > thread . ksp ;
else
stack = ( unsigned long * ) & stack ;
}
addr = ( unsigned long ) stack ;
endstack = ( unsigned long * ) PAGE_ALIGN ( addr ) ;
2020-06-09 07:31:05 +03:00
printk ( " %sStack from %08lx: " , loglvl , ( unsigned long ) stack ) ;
2014-11-06 10:19:40 +03:00
for ( i = 0 ; i < kstack_depth_to_print ; i + + ) {
if ( stack + 1 > endstack )
break ;
if ( i % 8 = = 0 )
2020-06-09 07:31:05 +03:00
printk ( " %s \n " , loglvl ) ;
printk ( " %s %08lx " , loglvl , * stack + + ) ;
2014-11-06 10:19:40 +03:00
}
2020-06-09 07:31:05 +03:00
printk ( " %s \n Call Trace: " , loglvl ) ;
2014-11-06 10:19:40 +03:00
i = 0 ;
while ( stack + 1 < = endstack ) {
addr = * stack + + ;
/*
* If the address is either in the text segment of the
* kernel , or in the region which contains vmalloc ' ed
* memory , it * may * be the address of a calling
* routine ; if so , print it so that someone tracing
* down the cause of the crash will be able to figure
* out the call path that was taken .
*/
if ( ( ( addr > = ( unsigned long ) _stext ) & &
( addr < = ( unsigned long ) _etext ) ) ) {
if ( i % 4 = = 0 )
pr_emerg ( " \n " ) ;
2020-06-09 07:31:05 +03:00
printk ( " %s [<%08lx>] " , loglvl , addr ) ;
2014-11-06 10:19:40 +03:00
i + + ;
}
}
2020-06-09 07:31:05 +03:00
printk ( " %s \n " , loglvl ) ;
}
2014-11-06 10:19:40 +03:00
void __init trap_init ( void )
{
/* Nothing to do here */
}
/* Breakpoint handler */
asmlinkage void breakpoint_c ( struct pt_regs * fp )
{
/*
* The breakpoint entry code has moved the PC on by 4 bytes , so we must
* move it back . This could be done on the host but we do it here
* because monitor . S of JTAG gdbserver does it too .
*/
fp - > ea - = 4 ;
_exception ( SIGTRAP , fp , TRAP_BRKPT , fp - > ea ) ;
}
# ifndef CONFIG_NIOS2_ALIGNMENT_TRAP
/* Alignment exception handler */
asmlinkage void handle_unaligned_c ( struct pt_regs * fp , int cause )
{
unsigned long addr = RDCTL ( CTL_BADADDR ) ;
cause > > = 2 ;
fp - > ea - = 4 ;
if ( fixup_exception ( fp ) )
return ;
if ( ! user_mode ( fp ) ) {
pr_alert ( " Unaligned access from kernel mode, this might be a hardware \n " ) ;
pr_alert ( " problem, dump registers and restart the instruction \n " ) ;
pr_alert ( " BADADDR 0x%08lx \n " , addr ) ;
pr_alert ( " cause %d \n " , cause ) ;
pr_alert ( " op-code 0x%08lx \n " , * ( unsigned long * ) ( fp - > ea ) ) ;
show_regs ( fp ) ;
return ;
}
_exception ( SIGBUS , fp , BUS_ADRALN , addr ) ;
}
# endif /* CONFIG_NIOS2_ALIGNMENT_TRAP */
/* Illegal instruction handler */
asmlinkage void handle_illegal_c ( struct pt_regs * fp )
{
fp - > ea - = 4 ;
_exception ( SIGILL , fp , ILL_ILLOPC , fp - > ea ) ;
}
/* Supervisor instruction handler */
asmlinkage void handle_supervisor_instr ( struct pt_regs * fp )
{
fp - > ea - = 4 ;
_exception ( SIGILL , fp , ILL_PRVOPC , fp - > ea ) ;
}
/* Division error handler */
asmlinkage void handle_diverror_c ( struct pt_regs * fp )
{
fp - > ea - = 4 ;
_exception ( SIGFPE , fp , FPE_INTDIV , fp - > ea ) ;
}
/* Unhandled exception handler */
asmlinkage void unhandled_exception ( struct pt_regs * regs , int cause )
{
unsigned long addr = RDCTL ( CTL_BADADDR ) ;
cause / = 4 ;
pr_emerg ( " Unhandled exception #%d in %s mode (badaddr=0x%08lx) \n " ,
cause , user_mode ( regs ) ? " user " : " kernel " , addr ) ;
regs - > ea - = 4 ;
show_regs ( regs ) ;
pr_emerg ( " opcode: 0x%08lx \n " , * ( unsigned long * ) ( regs - > ea ) ) ;
}
2015-04-16 10:19:01 +03:00
asmlinkage void handle_trap_1_c ( struct pt_regs * fp )
{
_send_sig ( SIGUSR1 , 0 , fp - > ea ) ;
}
asmlinkage void handle_trap_2_c ( struct pt_regs * fp )
{
_send_sig ( SIGUSR2 , 0 , fp - > ea ) ;
}
asmlinkage void handle_trap_3_c ( struct pt_regs * fp )
{
_send_sig ( SIGILL , ILL_ILLTRP , fp - > ea ) ;
}