2009-03-27 16:25:13 +03:00
/*
* HW exception handling
*
* Copyright ( C ) 2008 - 2009 Michal Simek < monstr @ monstr . eu >
* Copyright ( C ) 2008 PetaLogix
*
* 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 .
*/
/*
* This file handles the architecture - dependent parts of hardware exceptions
*/
# include <linux/kernel.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/kallsyms.h>
# include <linux/module.h>
# include <asm/exceptions.h>
# include <asm/entry.h> /* For KM CPU var */
2009-05-26 18:30:24 +04:00
# include <linux/uaccess.h>
# include <linux/errno.h>
# include <linux/ptrace.h>
2009-03-27 16:25:13 +03:00
# include <asm/current.h>
# define MICROBLAZE_ILL_OPCODE_EXCEPTION 0x02
# define MICROBLAZE_IBUS_EXCEPTION 0x03
# define MICROBLAZE_DBUS_EXCEPTION 0x04
# define MICROBLAZE_DIV_ZERO_EXCEPTION 0x05
# define MICROBLAZE_FPU_EXCEPTION 0x06
2009-05-26 18:30:24 +04:00
# define MICROBLAZE_PRIVILEGED_EXCEPTION 0x07
2009-03-27 16:25:13 +03:00
static DEFINE_SPINLOCK ( die_lock ) ;
void die ( const char * str , struct pt_regs * fp , long err )
{
console_verbose ( ) ;
spin_lock_irq ( & die_lock ) ;
printk ( KERN_WARNING " Oops: %s, sig: %ld \n " , str , err ) ;
show_regs ( fp ) ;
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 signr , struct pt_regs * regs , int code , unsigned long addr )
{
siginfo_t info ;
if ( kernel_mode ( regs ) ) {
debugger ( regs ) ;
die ( " Exception in kernel mode " , regs , signr ) ;
}
info . si_signo = signr ;
info . si_errno = 0 ;
info . si_code = code ;
info . si_addr = ( void __user * ) addr ;
force_sig_info ( signr , & info , current ) ;
}
asmlinkage void full_exception ( struct pt_regs * regs , unsigned int type ,
int fsr , int addr )
{
2009-05-26 18:30:24 +04:00
# ifdef CONFIG_MMU
int code ;
addr = regs - > pc ;
# endif
2009-03-27 16:25:13 +03:00
#if 0
2009-09-15 11:49:53 +04:00
printk ( KERN_WARNING " Exception %02x in %s mode, FSR=%08x PC=%08x " \
" ESR=%08x \n " ,
2009-03-27 16:25:13 +03:00
type , user_mode ( regs ) ? " user " : " kernel " , fsr ,
( unsigned int ) regs - > pc , ( unsigned int ) regs - > esr ) ;
# endif
switch ( type & 0x1F ) {
case MICROBLAZE_ILL_OPCODE_EXCEPTION :
2009-05-26 18:30:24 +04:00
if ( user_mode ( regs ) ) {
2009-09-15 11:49:53 +04:00
pr_debug ( KERN_WARNING " Illegal opcode exception " \
" in user mode. \n " ) ;
2009-05-26 18:30:24 +04:00
_exception ( SIGILL , regs , ILL_ILLOPC , addr ) ;
return ;
}
2009-09-15 11:49:53 +04:00
printk ( KERN_WARNING " Illegal opcode exception " \
" in kernel mode. \n " ) ;
2009-05-26 18:30:24 +04:00
die ( " opcode exception " , regs , SIGBUS ) ;
2009-03-27 16:25:13 +03:00
break ;
case MICROBLAZE_IBUS_EXCEPTION :
if ( user_mode ( regs ) ) {
2009-09-15 11:49:53 +04:00
pr_debug ( KERN_WARNING " Instruction bus error " \
" exception in user mode. \n " ) ;
2009-03-27 16:25:13 +03:00
_exception ( SIGBUS , regs , BUS_ADRERR , addr ) ;
return ;
}
2009-09-15 11:49:53 +04:00
printk ( KERN_WARNING " Instruction bus error exception " \
" in kernel mode. \n " ) ;
2009-03-27 16:25:13 +03:00
die ( " bus exception " , regs , SIGBUS ) ;
break ;
case MICROBLAZE_DBUS_EXCEPTION :
if ( user_mode ( regs ) ) {
2009-09-15 11:49:53 +04:00
pr_debug ( KERN_WARNING " Data bus error exception " \
" in user mode. \n " ) ;
2009-03-27 16:25:13 +03:00
_exception ( SIGBUS , regs , BUS_ADRERR , addr ) ;
return ;
}
2009-09-15 11:49:53 +04:00
printk ( KERN_WARNING " Data bus error exception " \
" in kernel mode. \n " ) ;
2009-03-27 16:25:13 +03:00
die ( " bus exception " , regs , SIGBUS ) ;
break ;
case MICROBLAZE_DIV_ZERO_EXCEPTION :
2009-05-26 18:30:24 +04:00
if ( user_mode ( regs ) ) {
2009-09-15 11:49:53 +04:00
pr_debug ( KERN_WARNING " Divide by zero exception " \
" in user mode \n " ) ;
2009-09-22 10:58:47 +04:00
_exception ( SIGILL , regs , FPE_INTDIV , addr ) ;
2009-05-26 18:30:24 +04:00
return ;
}
2009-09-15 11:49:53 +04:00
printk ( KERN_WARNING " Divide by zero exception " \
" in kernel mode. \n " ) ;
2009-05-26 18:30:24 +04:00
die ( " Divide by exception " , regs , SIGBUS ) ;
2009-03-27 16:25:13 +03:00
break ;
case MICROBLAZE_FPU_EXCEPTION :
2009-09-15 11:49:53 +04:00
pr_debug ( KERN_WARNING " FPU exception \n " ) ;
2009-03-27 16:25:13 +03:00
/* IEEE FP exception */
/* I removed fsr variable and use code var for storing fsr */
if ( fsr & FSR_IO )
fsr = FPE_FLTINV ;
else if ( fsr & FSR_OF )
fsr = FPE_FLTOVF ;
else if ( fsr & FSR_UF )
fsr = FPE_FLTUND ;
else if ( fsr & FSR_DZ )
fsr = FPE_FLTDIV ;
else if ( fsr & FSR_DO )
fsr = FPE_FLTRES ;
_exception ( SIGFPE , regs , fsr , addr ) ;
break ;
2009-05-26 18:30:24 +04:00
# ifdef CONFIG_MMU
case MICROBLAZE_PRIVILEGED_EXCEPTION :
2009-09-15 11:49:53 +04:00
pr_debug ( KERN_WARNING " Privileged exception \n " ) ;
2009-05-26 18:30:24 +04:00
/* "brk r0,r0" - used as debug breakpoint */
if ( get_user ( code , ( unsigned long * ) regs - > pc ) = = 0
& & code = = 0x980c0000 ) {
_exception ( SIGTRAP , regs , TRAP_BRKPT , addr ) ;
} else {
_exception ( SIGILL , regs , ILL_PRVOPC , addr ) ;
}
break ;
# endif
2009-03-27 16:25:13 +03:00
default :
2009-05-26 18:30:24 +04:00
/* FIXME what to do in unexpected exception */
2009-03-27 16:25:13 +03:00
printk ( KERN_WARNING " Unexpected exception %02x "
" PC=%08x in %s mode \n " , type , ( unsigned int ) addr ,
kernel_mode ( regs ) ? " kernel " : " user " ) ;
}
return ;
}