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>
2014-10-06 17:53:53 +02:00
# include <linux/slab.h>
# include <asm/switch_to.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 )
{
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 ) ;
}
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 ;
2014-11-19 13:31:08 +01:00
printk ( " User process fault: interruption code %04x ilc:%d " ,
regs - > int_code & 0xffff , regs - > int_code > > 17 ) ;
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 ;
}
2014-09-22 16:39:06 +02:00
void do_report_trap ( struct pt_regs * regs , int si_signo , int si_code , char * str )
2011-12-27 11:27:18 +01:00
{
siginfo_t info ;
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
}
}
2014-10-22 12:42:38 +02:00
static void do_trap ( struct pt_regs * regs , int si_signo , int si_code , char * str )
2014-09-22 16:39:06 +02:00
{
if ( notify_die ( DIE_TRAP , str , regs , 0 ,
regs - > int_code , si_signo ) = = NOTIFY_STOP )
return ;
do_report_trap ( regs , si_signo , si_code , str ) ;
}
2014-10-22 12:42:38 +02:00
NOKPROBE_SYMBOL ( do_trap ) ;
2014-09-22 16:39:06 +02:00
2014-10-22 12:42:38 +02:00
void 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
}
2014-10-22 12:42:38 +02:00
NOKPROBE_SYMBOL ( do_per_trap ) ;
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 " )
2012-07-31 11:03:04 +02:00
DO_ERROR_INFO ( transaction_exception , SIGILL , ILL_ILLOPN ,
" transaction constraint exception " )
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
}
2014-11-19 14:05:52 +01:00
void translation_exception ( struct pt_regs * regs )
{
/* May never happen. */
2015-02-18 14:17:14 +01:00
panic ( " Translation exception " ) ;
2014-11-19 14:05:52 +01:00
}
2014-10-22 12:42:38 +02:00
void 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 ;
2014-09-22 16:39:06 +02:00
int is_uprobe_insn = 0 ;
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 ;
2014-09-22 16:39:06 +02:00
# ifdef CONFIG_UPROBES
} else if ( * ( ( __u16 * ) opcode ) = = UPROBE_SWBP_INSN ) {
is_uprobe_insn = 1 ;
2005-04-16 15:20:36 -07:00
# endif
} else
signal = SIGILL ;
2014-09-22 16:39:06 +02:00
}
/*
* We got either an illegal op in kernel mode , or user space trapped
* on a uprobes illegal instruction . See if kprobes or uprobes picks
* it up . If not , SIGILL .
*/
if ( is_uprobe_insn | | ! user_mode ( regs ) ) {
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 ;
}
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
}
2014-10-22 12:42:38 +02:00
NOKPROBE_SYMBOL ( illegal_op ) ;
2005-04-16 15:20:36 -07:00
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
2014-10-06 17:53:53 +02:00
int alloc_vector_registers ( struct task_struct * tsk )
{
__vector128 * vxrs ;
int i ;
/* Allocate vector register save area. */
vxrs = kzalloc ( sizeof ( __vector128 ) * __NUM_VXRS ,
GFP_KERNEL | __GFP_REPEAT ) ;
if ( ! vxrs )
return - ENOMEM ;
preempt_disable ( ) ;
if ( tsk = = current )
save_fp_regs ( tsk - > thread . fp_regs . fprs ) ;
/* Copy the 16 floating point registers */
for ( i = 0 ; i < 16 ; i + + )
* ( freg_t * ) & vxrs [ i ] = tsk - > thread . fp_regs . fprs [ i ] ;
tsk - > thread . vxrs = vxrs ;
if ( tsk = = current ) {
__ctl_set_bit ( 0 , 17 ) ;
restore_vx_regs ( vxrs ) ;
}
preempt_enable ( ) ;
return 0 ;
}
void vector_exception ( struct pt_regs * regs )
{
int si_code , vic ;
if ( ! MACHINE_HAS_VX ) {
do_trap ( regs , SIGILL , ILL_ILLOPN , " illegal operation " ) ;
return ;
}
/* get vector interrupt code from fpc */
asm volatile ( " stfpc %0 " : " =m " ( current - > thread . fp_regs . fpc ) ) ;
vic = ( current - > thread . fp_regs . fpc & 0xf00 ) > > 8 ;
switch ( vic ) {
case 1 : /* invalid vector operation */
si_code = FPE_FLTINV ;
break ;
case 2 : /* division by zero */
si_code = FPE_FLTDIV ;
break ;
case 3 : /* overflow */
si_code = FPE_FLTOVF ;
break ;
case 4 : /* underflow */
si_code = FPE_FLTUND ;
break ;
case 5 : /* inexact */
si_code = FPE_FLTRES ;
break ;
default : /* unknown cause */
si_code = 0 ;
}
do_trap ( regs , SIGFPE , si_code , " vector exception " ) ;
}
static int __init disable_vector_extension ( char * str )
{
S390_lowcore . machine_flags & = ~ MACHINE_FLAG_VX ;
return 1 ;
}
__setup ( " novx " , disable_vector_extension ) ;
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
2015-02-12 13:08:27 +01:00
asm volatile ( " stfpc %0 " : " =m " ( current - > thread . fp_regs . fpc ) ) ;
2014-10-06 17:53:53 +02:00
/* Check for vector register enablement */
if ( MACHINE_HAS_VX & & ! current - > thread . vxrs & &
( current - > thread . fp_regs . fpc & FPC_DXC_MASK ) = = 0xfe00 ) {
alloc_vector_registers ( current ) ;
/* Vector data exception is suppressing, rewind psw. */
regs - > psw . addr = __rewind_psw ( regs - > psw , regs - > int_code > > 16 ) ;
clear_pt_regs_flag ( regs , PIF_PER_TRAP ) ;
return ;
}
2005-04-16 15:20:36 -07:00
if ( current - > thread . fp_regs . fpc & FPC_DXC_MASK )
signal = SIGFPE ;
else
signal = SIGILL ;
2015-02-12 13:08:27 +01:00
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
}
2014-10-22 12:42:38 +02:00
void 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. " ) ;
}
2014-10-22 12:42:38 +02:00
NOKPROBE_SYMBOL ( kernel_stack_overflow ) ;
2005-04-16 15:20:36 -07:00
void __init trap_init ( void )
{
2011-01-05 12:48:00 +01:00
local_mcck_enable ( ) ;
2005-04-16 15:20:36 -07:00
}