2005-04-16 15:20:36 -07:00
/*
* S390 version
2012-07-20 11:15:04 +02:00
* Copyright IBM Corp . 1999 , 2000
2005-04-16 15:20:36 -07:00
* Author ( s ) : Martin Schwidefsky ( schwidefsky @ de . ibm . com ) ,
* Denis Joseph Barrow ( djbarrow @ de . ibm . com , barrow_dj @ yahoo . com ) ,
*
* Derived from " arch/i386/kernel/traps.c "
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*/
/*
* ' Traps . c ' handles hardware traps and faults after we have saved some
* state in ' asm . s ' .
*/
2013-03-14 13:44:25 +01:00
# include <linux/kprobes.h>
# include <linux/kdebug.h>
# include <linux/module.h>
2011-07-24 10:48:33 +02:00
# include <linux/ptrace.h>
2013-03-14 13:44:25 +01:00
# include <linux/sched.h>
2005-04-16 15:20:36 -07:00
# include <linux/mm.h>
2008-04-17 07:46:26 +02:00
# include "entry.h"
2005-04-16 15:20:36 -07:00
2011-12-27 11:27:31 +01:00
int show_unhandled_signals = 1 ;
2005-04-16 15:20:36 -07:00
2012-07-31 11:03:04 +02:00
static inline void __user * get_trap_ip ( struct pt_regs * regs )
{
# ifdef CONFIG_64BIT
unsigned long address ;
if ( regs - > int_code & 0x200 )
address = * ( unsigned long * ) ( current - > thread . trap_tdb + 24 ) ;
else
address = regs - > psw . addr ;
return ( void __user * )
( ( address - ( regs - > int_code > > 16 ) ) & PSW_ADDR_INSN ) ;
# else
return ( void __user * )
( ( regs - > psw . addr - ( regs - > int_code > > 16 ) ) & PSW_ADDR_INSN ) ;
# endif
}
2011-12-27 11:27:18 +01:00
static inline void report_user_fault ( struct pt_regs * regs , int signr )
2005-04-16 15:20:36 -07:00
{
2010-05-17 10:00:21 +02:00
if ( ( task_pid_nr ( current ) > 1 ) & & ! show_unhandled_signals )
2005-04-16 15:20:36 -07:00
return ;
2010-05-17 10:00:21 +02:00
if ( ! unhandled_signal ( current , signr ) )
return ;
if ( ! printk_ratelimit ( ) )
return ;
2011-12-27 11:27:18 +01:00
printk ( " User process fault: interruption code 0x%X " , regs - > int_code ) ;
2010-05-17 10:00:21 +02:00
print_vma_addr ( " in " , regs - > psw . addr & PSW_ADDR_INSN ) ;
printk ( " \n " ) ;
2005-04-16 15:20:36 -07:00
show_regs ( regs ) ;
}
2007-04-27 16:01:42 +02:00
int is_valid_bugaddr ( unsigned long addr )
{
return 1 ;
}
2011-12-27 11:27:18 +01:00
static void __kprobes do_trap ( struct pt_regs * regs ,
int si_signo , int si_code , char * str )
{
siginfo_t info ;
if ( notify_die ( DIE_TRAP , str , regs , 0 ,
regs - > int_code , si_signo ) = = NOTIFY_STOP )
2006-09-20 15:58:39 +02:00
return ;
2012-07-27 10:31:12 +02:00
if ( user_mode ( regs ) ) {
2011-12-27 11:27:18 +01:00
info . si_signo = si_signo ;
info . si_errno = 0 ;
info . si_code = si_code ;
2012-07-31 11:03:04 +02:00
info . si_addr = get_trap_ip ( regs ) ;
2011-12-27 11:27:18 +01:00
force_sig_info ( si_signo , & info , current ) ;
report_user_fault ( regs , si_signo ) ;
2005-04-16 15:20:36 -07:00
} else {
const struct exception_table_entry * fixup ;
fixup = search_exception_tables ( regs - > psw . addr & PSW_ADDR_INSN ) ;
if ( fixup )
2012-09-05 13:26:11 +02:00
regs - > psw . addr = extable_fixup ( fixup ) | PSW_ADDR_AMODE ;
2007-04-27 16:01:42 +02:00
else {
enum bug_trap_type btt ;
2007-07-15 23:41:39 -07:00
btt = report_bug ( regs - > psw . addr & PSW_ADDR_INSN , regs ) ;
2007-04-27 16:01:42 +02:00
if ( btt = = BUG_TRAP_TYPE_WARN )
return ;
2011-12-27 11:27:18 +01:00
die ( regs , str ) ;
2007-04-27 16:01:42 +02:00
}
2005-04-16 15:20:36 -07:00
}
}
2011-01-05 12:48:10 +01:00
void __kprobes do_per_trap ( struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
2011-07-24 10:48:33 +02:00
siginfo_t info ;
2011-01-05 12:48:10 +01:00
if ( notify_die ( DIE_SSTEP , " sstep " , regs , 0 , 0 , SIGTRAP ) = = NOTIFY_STOP )
2006-09-20 15:58:39 +02:00
return ;
2011-07-24 10:48:33 +02:00
if ( ! current - > ptrace )
return ;
info . si_signo = SIGTRAP ;
info . si_errno = 0 ;
info . si_code = TRAP_HWBKPT ;
2011-10-30 15:17:15 +01:00
info . si_addr =
( void __force __user * ) current - > thread . per_event . address ;
2011-07-24 10:48:33 +02:00
force_sig_info ( SIGTRAP , & info , current ) ;
2005-04-16 15:20:36 -07:00
}
2012-10-18 18:10:06 +02:00
void default_trap_handler ( struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
2012-07-27 10:31:12 +02:00
if ( user_mode ( regs ) ) {
2011-12-27 11:27:18 +01:00
report_user_fault ( regs , SIGSEGV ) ;
2010-05-17 10:00:13 +02:00
do_exit ( SIGSEGV ) ;
2005-04-16 15:20:36 -07:00
} else
2011-12-27 11:27:18 +01:00
die ( regs , " Unknown program exception " ) ;
2005-04-16 15:20:36 -07:00
}
2010-10-25 16:10:37 +02:00
# define DO_ERROR_INFO(name, signr, sicode, str) \
2012-10-18 18:10:06 +02:00
void name ( struct pt_regs * regs ) \
{ \
do_trap ( regs , signr , sicode , str ) ; \
2005-04-16 15:20:36 -07:00
}
2010-10-25 16:10:37 +02:00
DO_ERROR_INFO ( addressing_exception , SIGILL , ILL_ILLADR ,
" addressing exception " )
DO_ERROR_INFO ( execute_exception , SIGILL , ILL_ILLOPN ,
" execute exception " )
DO_ERROR_INFO ( divide_exception , SIGFPE , FPE_INTDIV ,
" fixpoint divide exception " )
DO_ERROR_INFO ( overflow_exception , SIGFPE , FPE_INTOVF ,
" fixpoint overflow exception " )
DO_ERROR_INFO ( hfp_overflow_exception , SIGFPE , FPE_FLTOVF ,
" HFP overflow exception " )
DO_ERROR_INFO ( hfp_underflow_exception , SIGFPE , FPE_FLTUND ,
" HFP underflow exception " )
DO_ERROR_INFO ( hfp_significance_exception , SIGFPE , FPE_FLTRES ,
" HFP significance exception " )
DO_ERROR_INFO ( hfp_divide_exception , SIGFPE , FPE_FLTDIV ,
" HFP divide exception " )
DO_ERROR_INFO ( hfp_sqrt_exception , SIGFPE , FPE_FLTINV ,
" HFP square root exception " )
DO_ERROR_INFO ( operand_exception , SIGILL , ILL_ILLOPN ,
" operand exception " )
DO_ERROR_INFO ( privileged_op , SIGILL , ILL_PRVOPC ,
" privileged operation " )
DO_ERROR_INFO ( special_op_exception , SIGILL , ILL_ILLOPN ,
" special operation exception " )
DO_ERROR_INFO ( translation_exception , SIGILL , ILL_ILLOPN ,
" translation exception " )
2012-07-31 11:03:04 +02:00
# ifdef CONFIG_64BIT
DO_ERROR_INFO ( transaction_exception , SIGILL , ILL_ILLOPN ,
" transaction constraint exception " )
# endif
2011-12-27 11:27:18 +01:00
static inline void do_fp_trap ( struct pt_regs * regs , int fpc )
2005-04-16 15:20:36 -07:00
{
2011-12-27 11:27:18 +01:00
int si_code = 0 ;
2005-04-16 15:20:36 -07:00
/* FPC[2] is Data Exception Code */
if ( ( fpc & 0x00000300 ) = = 0 ) {
/* bits 6 and 7 of DXC are 0 iff IEEE exception */
if ( fpc & 0x8000 ) /* invalid fp operation */
2011-12-27 11:27:18 +01:00
si_code = FPE_FLTINV ;
2005-04-16 15:20:36 -07:00
else if ( fpc & 0x4000 ) /* div by 0 */
2011-12-27 11:27:18 +01:00
si_code = FPE_FLTDIV ;
2005-04-16 15:20:36 -07:00
else if ( fpc & 0x2000 ) /* overflow */
2011-12-27 11:27:18 +01:00
si_code = FPE_FLTOVF ;
2005-04-16 15:20:36 -07:00
else if ( fpc & 0x1000 ) /* underflow */
2011-12-27 11:27:18 +01:00
si_code = FPE_FLTUND ;
2005-04-16 15:20:36 -07:00
else if ( fpc & 0x0800 ) /* inexact */
2011-12-27 11:27:18 +01:00
si_code = FPE_FLTRES ;
2005-04-16 15:20:36 -07:00
}
2011-12-27 11:27:18 +01:00
do_trap ( regs , SIGFPE , si_code , " floating point exception " ) ;
2005-04-16 15:20:36 -07:00
}
2012-10-18 18:10:06 +02:00
void __kprobes illegal_op ( struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
siginfo_t info ;
__u8 opcode [ 6 ] ;
2006-07-12 16:41:55 +02:00
__u16 __user * location ;
2005-04-16 15:20:36 -07:00
int signal = 0 ;
2012-07-31 11:03:04 +02:00
location = get_trap_ip ( regs ) ;
2005-04-16 15:20:36 -07:00
2012-07-27 10:31:12 +02:00
if ( user_mode ( regs ) ) {
2006-10-27 12:39:22 +02:00
if ( get_user ( * ( ( __u16 * ) opcode ) , ( __u16 __user * ) location ) )
return ;
2005-04-16 15:20:36 -07:00
if ( * ( ( __u16 * ) opcode ) = = S390_BREAKPOINT_U16 ) {
2011-07-24 10:48:33 +02:00
if ( current - > ptrace ) {
info . si_signo = SIGTRAP ;
info . si_errno = 0 ;
info . si_code = TRAP_BRKPT ;
info . si_addr = location ;
force_sig_info ( SIGTRAP , & info , current ) ;
} else
2005-04-16 15:20:36 -07:00
signal = SIGILL ;
# ifdef CONFIG_MATHEMU
} else if ( opcode [ 0 ] = = 0xb3 ) {
2006-10-27 12:39:22 +02:00
if ( get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) )
return ;
2005-04-16 15:20:36 -07:00
signal = math_emu_b3 ( opcode , regs ) ;
} else if ( opcode [ 0 ] = = 0xed ) {
2006-10-27 12:39:22 +02:00
if ( get_user ( * ( ( __u32 * ) ( opcode + 2 ) ) ,
( __u32 __user * ) ( location + 1 ) ) )
return ;
2005-04-16 15:20:36 -07:00
signal = math_emu_ed ( opcode , regs ) ;
} else if ( * ( ( __u16 * ) opcode ) = = 0xb299 ) {
2006-10-27 12:39:22 +02:00
if ( get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) )
return ;
2005-04-16 15:20:36 -07:00
signal = math_emu_srnm ( opcode , regs ) ;
} else if ( * ( ( __u16 * ) opcode ) = = 0xb29c ) {
2006-10-27 12:39:22 +02:00
if ( get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) )
return ;
2005-04-16 15:20:36 -07:00
signal = math_emu_stfpc ( opcode , regs ) ;
} else if ( * ( ( __u16 * ) opcode ) = = 0xb29d ) {
2006-10-27 12:39:22 +02:00
if ( get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) )
return ;
2005-04-16 15:20:36 -07:00
signal = math_emu_lfpc ( opcode , regs ) ;
# endif
} else
signal = SIGILL ;
2007-02-05 21:17:29 +01:00
} else {
/*
* If we get an illegal op in kernel mode , send it through the
* kprobes notifier . If kprobes doesn ' t pick it up , SIGILL
*/
2011-12-27 11:27:18 +01:00
if ( notify_die ( DIE_BPT , " bpt " , regs , 0 ,
2007-02-05 21:17:29 +01:00
3 , SIGTRAP ) ! = NOTIFY_STOP )
signal = SIGILL ;
}
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_MATHEMU
if ( signal = = SIGFPE )
2011-12-27 11:27:18 +01:00
do_fp_trap ( regs , current - > thread . fp_regs . fpc ) ;
else if ( signal = = SIGSEGV )
do_trap ( regs , signal , SEGV_MAPERR , " user address fault " ) ;
else
2005-04-16 15:20:36 -07:00
# endif
2011-12-27 11:27:18 +01:00
if ( signal )
do_trap ( regs , signal , ILL_ILLOPC , " illegal operation " ) ;
2005-04-16 15:20:36 -07:00
}
# ifdef CONFIG_MATHEMU
2011-12-27 11:27:18 +01:00
void specification_exception ( struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
__u8 opcode [ 6 ] ;
2006-10-09 20:28:03 +01:00
__u16 __user * location = NULL ;
2005-04-16 15:20:36 -07:00
int signal = 0 ;
2012-07-31 11:03:04 +02:00
location = ( __u16 __user * ) get_trap_ip ( regs ) ;
2005-04-16 15:20:36 -07:00
2012-07-27 10:31:12 +02:00
if ( user_mode ( regs ) ) {
2005-04-16 15:20:36 -07:00
get_user ( * ( ( __u16 * ) opcode ) , location ) ;
switch ( opcode [ 0 ] ) {
case 0x28 : /* LDR Rx,Ry */
signal = math_emu_ldr ( opcode ) ;
break ;
case 0x38 : /* LER Rx,Ry */
signal = math_emu_ler ( opcode ) ;
break ;
case 0x60 : /* STD R,D(X,B) */
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_std ( opcode , regs ) ;
break ;
case 0x68 : /* LD R,D(X,B) */
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_ld ( opcode , regs ) ;
break ;
case 0x70 : /* STE R,D(X,B) */
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_ste ( opcode , regs ) ;
break ;
case 0x78 : /* LE R,D(X,B) */
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_le ( opcode , regs ) ;
break ;
default :
signal = SIGILL ;
break ;
}
} else
signal = SIGILL ;
if ( signal = = SIGFPE )
2011-12-27 11:27:18 +01:00
do_fp_trap ( regs , current - > thread . fp_regs . fpc ) ;
else if ( signal )
do_trap ( regs , signal , ILL_ILLOPN , " specification exception " ) ;
2005-04-16 15:20:36 -07:00
}
# else
2010-10-25 16:10:37 +02:00
DO_ERROR_INFO ( specification_exception , SIGILL , ILL_ILLOPN ,
" specification exception " ) ;
2005-04-16 15:20:36 -07:00
# endif
2012-10-18 18:10:06 +02:00
void data_exception ( struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
2006-07-12 16:41:55 +02:00
__u16 __user * location ;
2005-04-16 15:20:36 -07:00
int signal = 0 ;
2012-07-31 11:03:04 +02:00
location = get_trap_ip ( regs ) ;
2005-04-16 15:20:36 -07:00
if ( MACHINE_HAS_IEEE )
2006-09-28 16:56:43 +02:00
asm volatile ( " stfpc %0 " : " =m " ( current - > thread . fp_regs . fpc ) ) ;
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_MATHEMU
2012-07-27 10:31:12 +02:00
else if ( user_mode ( regs ) ) {
2005-04-16 15:20:36 -07:00
__u8 opcode [ 6 ] ;
get_user ( * ( ( __u16 * ) opcode ) , location ) ;
switch ( opcode [ 0 ] ) {
case 0x28 : /* LDR Rx,Ry */
signal = math_emu_ldr ( opcode ) ;
break ;
case 0x38 : /* LER Rx,Ry */
signal = math_emu_ler ( opcode ) ;
break ;
case 0x60 : /* STD R,D(X,B) */
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_std ( opcode , regs ) ;
break ;
case 0x68 : /* LD R,D(X,B) */
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_ld ( opcode , regs ) ;
break ;
case 0x70 : /* STE R,D(X,B) */
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_ste ( opcode , regs ) ;
break ;
case 0x78 : /* LE R,D(X,B) */
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_le ( opcode , regs ) ;
break ;
case 0xb3 :
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_b3 ( opcode , regs ) ;
break ;
case 0xed :
get_user ( * ( ( __u32 * ) ( opcode + 2 ) ) ,
2006-10-09 20:28:03 +01:00
( __u32 __user * ) ( location + 1 ) ) ;
2005-04-16 15:20:36 -07:00
signal = math_emu_ed ( opcode , regs ) ;
break ;
case 0xb2 :
if ( opcode [ 1 ] = = 0x99 ) {
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_srnm ( opcode , regs ) ;
} else if ( opcode [ 1 ] = = 0x9c ) {
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_stfpc ( opcode , regs ) ;
} else if ( opcode [ 1 ] = = 0x9d ) {
get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) ;
signal = math_emu_lfpc ( opcode , regs ) ;
} else
signal = SIGILL ;
break ;
default :
signal = SIGILL ;
break ;
}
}
# endif
if ( current - > thread . fp_regs . fpc & FPC_DXC_MASK )
signal = SIGFPE ;
else
signal = SIGILL ;
if ( signal = = SIGFPE )
2011-12-27 11:27:18 +01:00
do_fp_trap ( regs , current - > thread . fp_regs . fpc ) ;
else if ( signal )
do_trap ( regs , signal , ILL_ILLOPN , " data exception " ) ;
2005-04-16 15:20:36 -07:00
}
2012-10-18 18:10:06 +02:00
void space_switch_exception ( struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
/* Set user psw back to home space mode. */
2012-07-27 10:31:12 +02:00
if ( user_mode ( regs ) )
2005-04-16 15:20:36 -07:00
regs - > psw . mask | = PSW_ASC_HOME ;
/* Send SIGILL. */
2011-12-27 11:27:18 +01:00
do_trap ( regs , SIGILL , ILL_PRVOPC , " space switch event " ) ;
2005-04-16 15:20:36 -07:00
}
2011-07-24 10:48:24 +02:00
void __kprobes kernel_stack_overflow ( struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
2005-06-21 17:16:28 -07:00
bust_spinlocks ( 1 ) ;
printk ( " Kernel stack overflow. \n " ) ;
show_regs ( regs ) ;
bust_spinlocks ( 0 ) ;
2005-04-16 15:20:36 -07:00
panic ( " Corrupt kernel stack, can't continue. " ) ;
}
void __init trap_init ( void )
{
2011-01-05 12:48:00 +01:00
local_mcck_enable ( ) ;
2005-04-16 15:20:36 -07:00
}