2005-04-17 02:20:36 +04:00
/*
* arch / s390 / kernel / traps . c
*
* S390 version
* Copyright ( C ) 1999 , 2000 IBM Deutschland Entwicklung GmbH , IBM Corporation
* 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 ' .
*/
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/ptrace.h>
# include <linux/timer.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/smp_lock.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/kallsyms.h>
2005-08-02 08:11:33 +04:00
# include <linux/reboot.h>
2006-09-20 17:58:39 +04:00
# include <linux/kprobes.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/uaccess.h>
# include <asm/io.h>
# include <asm/atomic.h>
# include <asm/mathemu.h>
# include <asm/cpcmd.h>
# include <asm/s390_ext.h>
# include <asm/lowcore.h>
# include <asm/debug.h>
2006-09-20 17:58:39 +04:00
# include <asm/kdebug.h>
2005-04-17 02:20:36 +04:00
/* Called from entry.S only */
extern void handle_per_exception ( struct pt_regs * regs ) ;
typedef void pgm_check_handler_t ( struct pt_regs * , long ) ;
pgm_check_handler_t * pgm_check_table [ 128 ] ;
# ifdef CONFIG_SYSCTL
# ifdef CONFIG_PROCESS_DEBUG
int sysctl_userprocess_debug = 1 ;
# else
int sysctl_userprocess_debug = 0 ;
# endif
# endif
extern pgm_check_handler_t do_protection_exception ;
extern pgm_check_handler_t do_dat_exception ;
# ifdef CONFIG_PFAULT
extern int pfault_init ( void ) ;
extern void pfault_fini ( void ) ;
2006-10-06 18:38:35 +04:00
extern void pfault_interrupt ( __u16 error_code ) ;
2005-04-17 02:20:36 +04:00
static ext_int_info_t ext_int_pfault ;
# endif
extern pgm_check_handler_t do_monitor_call ;
# define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; })
2006-01-06 11:19:28 +03:00
# ifndef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
# define FOURLONG "%08lx %08lx %08lx %08lx\n"
static int kstack_depth_to_print = 12 ;
2006-01-06 11:19:28 +03:00
# else /* CONFIG_64BIT */
2005-04-17 02:20:36 +04:00
# define FOURLONG "%016lx %016lx %016lx %016lx\n"
static int kstack_depth_to_print = 20 ;
2006-01-06 11:19:28 +03:00
# endif /* CONFIG_64BIT */
2005-04-17 02:20:36 +04:00
2006-09-20 17:58:39 +04:00
ATOMIC_NOTIFIER_HEAD ( s390die_chain ) ;
int register_die_notifier ( struct notifier_block * nb )
{
return atomic_notifier_chain_register ( & s390die_chain , nb ) ;
}
EXPORT_SYMBOL ( register_die_notifier ) ;
int unregister_die_notifier ( struct notifier_block * nb )
{
return atomic_notifier_chain_unregister ( & s390die_chain , nb ) ;
}
EXPORT_SYMBOL ( unregister_die_notifier ) ;
2005-04-17 02:20:36 +04:00
/*
* For show_trace we have tree different stack to consider :
* - the panic stack which is used if the kernel stack has overflown
* - the asynchronous interrupt stack ( cpu related )
* - the synchronous kernel stack ( process related )
* The stack trace can start at any of the three stack and can potentially
* touch all of them . The order is : panic stack , async stack , sync stack .
*/
static unsigned long
__show_trace ( unsigned long sp , unsigned long low , unsigned long high )
{
struct stack_frame * sf ;
struct pt_regs * regs ;
while ( 1 ) {
sp = sp & PSW_ADDR_INSN ;
if ( sp < low | | sp > high - sizeof ( * sf ) )
return sp ;
sf = ( struct stack_frame * ) sp ;
printk ( " ([<%016lx>] " , sf - > gprs [ 8 ] & PSW_ADDR_INSN ) ;
print_symbol ( " %s) \n " , sf - > gprs [ 8 ] & PSW_ADDR_INSN ) ;
/* Follow the backchain. */
while ( 1 ) {
low = sp ;
sp = sf - > back_chain & PSW_ADDR_INSN ;
if ( ! sp )
break ;
if ( sp < = low | | sp > high - sizeof ( * sf ) )
return sp ;
sf = ( struct stack_frame * ) sp ;
printk ( " [<%016lx>] " , sf - > gprs [ 8 ] & PSW_ADDR_INSN ) ;
print_symbol ( " %s \n " , sf - > gprs [ 8 ] & PSW_ADDR_INSN ) ;
}
/* Zero backchain detected, check for interrupt frame. */
sp = ( unsigned long ) ( sf + 1 ) ;
if ( sp < = low | | sp > high - sizeof ( * regs ) )
return sp ;
regs = ( struct pt_regs * ) sp ;
printk ( " [<%016lx>] " , regs - > psw . addr & PSW_ADDR_INSN ) ;
print_symbol ( " %s \n " , regs - > psw . addr & PSW_ADDR_INSN ) ;
low = sp ;
sp = regs - > gprs [ 15 ] ;
}
}
void show_trace ( struct task_struct * task , unsigned long * stack )
{
register unsigned long __r15 asm ( " 15 " ) ;
unsigned long sp ;
sp = ( unsigned long ) stack ;
if ( ! sp )
sp = task ? task - > thread . ksp : __r15 ;
printk ( " Call Trace: \n " ) ;
# ifdef CONFIG_CHECK_STACK
sp = __show_trace ( sp , S390_lowcore . panic_stack - 4096 ,
S390_lowcore . panic_stack ) ;
# endif
sp = __show_trace ( sp , S390_lowcore . async_stack - ASYNC_SIZE ,
S390_lowcore . async_stack ) ;
if ( task )
2006-01-12 12:05:50 +03:00
__show_trace ( sp , ( unsigned long ) task_stack_page ( task ) ,
( unsigned long ) task_stack_page ( task ) + THREAD_SIZE ) ;
2005-04-17 02:20:36 +04:00
else
__show_trace ( sp , S390_lowcore . thread_info ,
S390_lowcore . thread_info + THREAD_SIZE ) ;
printk ( " \n " ) ;
}
void show_stack ( struct task_struct * task , unsigned long * sp )
{
register unsigned long * __r15 asm ( " 15 " ) ;
unsigned long * stack ;
int i ;
if ( ! sp )
2006-06-29 16:56:23 +04:00
stack = task ? ( unsigned long * ) task - > thread . ksp : __r15 ;
else
stack = sp ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < kstack_depth_to_print ; i + + ) {
if ( ( ( addr_t ) stack & ( THREAD_SIZE - 1 ) ) = = 0 )
break ;
if ( i & & ( ( i * sizeof ( long ) % 32 ) = = 0 ) )
printk ( " \n " ) ;
printk ( " %p " , ( void * ) * stack + + ) ;
}
printk ( " \n " ) ;
show_trace ( task , sp ) ;
}
/*
* The architecture - independent dump_stack generator
*/
void dump_stack ( void )
{
2006-07-12 18:41:55 +04:00
show_stack ( NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( dump_stack ) ;
void show_registers ( struct pt_regs * regs )
{
mm_segment_t old_fs ;
char * mode ;
int i ;
mode = ( regs - > psw . mask & PSW_MASK_PSTATE ) ? " User " : " Krnl " ;
printk ( " %s PSW : %p %p " ,
mode , ( void * ) regs - > psw . mask ,
( void * ) regs - > psw . addr ) ;
print_symbol ( " (%s) \n " , regs - > psw . addr & PSW_ADDR_INSN ) ;
printk ( " %s GPRS: " FOURLONG , mode ,
regs - > gprs [ 0 ] , regs - > gprs [ 1 ] , regs - > gprs [ 2 ] , regs - > gprs [ 3 ] ) ;
printk ( " " FOURLONG ,
regs - > gprs [ 4 ] , regs - > gprs [ 5 ] , regs - > gprs [ 6 ] , regs - > gprs [ 7 ] ) ;
printk ( " " FOURLONG ,
regs - > gprs [ 8 ] , regs - > gprs [ 9 ] , regs - > gprs [ 10 ] , regs - > gprs [ 11 ] ) ;
printk ( " " FOURLONG ,
regs - > gprs [ 12 ] , regs - > gprs [ 13 ] , regs - > gprs [ 14 ] , regs - > gprs [ 15 ] ) ;
#if 0
/* FIXME: this isn't needed any more but it changes the ksymoops
* input . To remove or not to remove . . . */
save_access_regs ( regs - > acrs ) ;
printk ( " %s ACRS: %08x %08x %08x %08x \n " , mode ,
regs - > acrs [ 0 ] , regs - > acrs [ 1 ] , regs - > acrs [ 2 ] , regs - > acrs [ 3 ] ) ;
printk ( " %08x %08x %08x %08x \n " ,
regs - > acrs [ 4 ] , regs - > acrs [ 5 ] , regs - > acrs [ 6 ] , regs - > acrs [ 7 ] ) ;
printk ( " %08x %08x %08x %08x \n " ,
regs - > acrs [ 8 ] , regs - > acrs [ 9 ] , regs - > acrs [ 10 ] , regs - > acrs [ 11 ] ) ;
printk ( " %08x %08x %08x %08x \n " ,
regs - > acrs [ 12 ] , regs - > acrs [ 13 ] , regs - > acrs [ 14 ] , regs - > acrs [ 15 ] ) ;
# endif
/*
* Print the first 20 byte of the instruction stream at the
* time of the fault .
*/
old_fs = get_fs ( ) ;
if ( regs - > psw . mask & PSW_MASK_PSTATE )
set_fs ( USER_DS ) ;
else
set_fs ( KERNEL_DS ) ;
printk ( " %s Code: " , mode ) ;
for ( i = 0 ; i < 20 ; i + + ) {
unsigned char c ;
if ( __get_user ( c , ( char __user * ) ( regs - > psw . addr + i ) ) ) {
printk ( " Bad PSW. " ) ;
break ;
}
printk ( " %02x " , c ) ;
}
set_fs ( old_fs ) ;
printk ( " \n " ) ;
}
/* This is called from fs/proc/array.c */
char * task_show_regs ( struct task_struct * task , char * buffer )
{
struct pt_regs * regs ;
2006-01-12 12:05:49 +03:00
regs = task_pt_regs ( task ) ;
2005-04-17 02:20:36 +04:00
buffer + = sprintf ( buffer , " task: %p, ksp: %p \n " ,
task , ( void * ) task - > thread . ksp ) ;
buffer + = sprintf ( buffer , " User PSW : %p %p \n " ,
( void * ) regs - > psw . mask , ( void * ) regs - > psw . addr ) ;
buffer + = sprintf ( buffer , " User GPRS: " FOURLONG ,
regs - > gprs [ 0 ] , regs - > gprs [ 1 ] ,
regs - > gprs [ 2 ] , regs - > gprs [ 3 ] ) ;
buffer + = sprintf ( buffer , " " FOURLONG ,
regs - > gprs [ 4 ] , regs - > gprs [ 5 ] ,
regs - > gprs [ 6 ] , regs - > gprs [ 7 ] ) ;
buffer + = sprintf ( buffer , " " FOURLONG ,
regs - > gprs [ 8 ] , regs - > gprs [ 9 ] ,
regs - > gprs [ 10 ] , regs - > gprs [ 11 ] ) ;
buffer + = sprintf ( buffer , " " FOURLONG ,
regs - > gprs [ 12 ] , regs - > gprs [ 13 ] ,
regs - > gprs [ 14 ] , regs - > gprs [ 15 ] ) ;
buffer + = sprintf ( buffer , " User ACRS: %08x %08x %08x %08x \n " ,
task - > thread . acrs [ 0 ] , task - > thread . acrs [ 1 ] ,
task - > thread . acrs [ 2 ] , task - > thread . acrs [ 3 ] ) ;
buffer + = sprintf ( buffer , " %08x %08x %08x %08x \n " ,
task - > thread . acrs [ 4 ] , task - > thread . acrs [ 5 ] ,
task - > thread . acrs [ 6 ] , task - > thread . acrs [ 7 ] ) ;
buffer + = sprintf ( buffer , " %08x %08x %08x %08x \n " ,
task - > thread . acrs [ 8 ] , task - > thread . acrs [ 9 ] ,
task - > thread . acrs [ 10 ] , task - > thread . acrs [ 11 ] ) ;
buffer + = sprintf ( buffer , " %08x %08x %08x %08x \n " ,
task - > thread . acrs [ 12 ] , task - > thread . acrs [ 13 ] ,
task - > thread . acrs [ 14 ] , task - > thread . acrs [ 15 ] ) ;
return buffer ;
}
DEFINE_SPINLOCK ( die_lock ) ;
void die ( const char * str , struct pt_regs * regs , long err )
{
static int die_counter ;
debug_stop_all ( ) ;
console_verbose ( ) ;
spin_lock_irq ( & die_lock ) ;
bust_spinlocks ( 1 ) ;
printk ( " %s: %04lx [#%d] \n " , str , err & 0xffff , + + die_counter ) ;
show_regs ( regs ) ;
bust_spinlocks ( 0 ) ;
spin_unlock_irq ( & die_lock ) ;
if ( in_interrupt ( ) )
panic ( " Fatal exception in interrupt " ) ;
if ( panic_on_oops )
panic ( " Fatal exception: panic_on_oops " ) ;
do_exit ( SIGSEGV ) ;
}
static void inline
report_user_fault ( long interruption_code , struct pt_regs * regs )
{
# if defined(CONFIG_SYSCTL)
if ( ! sysctl_userprocess_debug )
return ;
# endif
# if defined(CONFIG_SYSCTL) || defined(CONFIG_PROCESS_DEBUG)
printk ( " User process fault: interruption code 0x%lX \n " ,
interruption_code ) ;
show_regs ( regs ) ;
# endif
}
2006-09-20 17:58:39 +04:00
static void __kprobes inline do_trap ( long interruption_code , int signr ,
char * str , struct pt_regs * regs ,
siginfo_t * info )
2005-04-17 02:20:36 +04:00
{
/*
* We got all needed information from the lowcore and can
* now safely switch on interrupts .
*/
if ( regs - > psw . mask & PSW_MASK_PSTATE )
local_irq_enable ( ) ;
2006-09-20 17:58:39 +04:00
if ( notify_die ( DIE_TRAP , str , regs , interruption_code ,
interruption_code , signr ) = = NOTIFY_STOP )
return ;
2005-04-17 02:20:36 +04:00
if ( regs - > psw . mask & PSW_MASK_PSTATE ) {
struct task_struct * tsk = current ;
tsk - > thread . trap_no = interruption_code & 0xffff ;
force_sig_info ( signr , info , tsk ) ;
report_user_fault ( interruption_code , regs ) ;
} else {
const struct exception_table_entry * fixup ;
fixup = search_exception_tables ( regs - > psw . addr & PSW_ADDR_INSN ) ;
if ( fixup )
regs - > psw . addr = fixup - > fixup | PSW_ADDR_AMODE ;
else
die ( str , regs , interruption_code ) ;
}
}
2006-07-12 18:41:55 +04:00
static inline void __user * get_check_address ( struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2006-07-12 18:41:55 +04:00
return ( void __user * ) ( ( regs - > psw . addr - S390_lowcore . pgm_ilc ) & PSW_ADDR_INSN ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-20 17:58:39 +04:00
void __kprobes do_single_step ( struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2006-09-20 17:58:39 +04:00
if ( notify_die ( DIE_SSTEP , " sstep " , regs , 0 , 0 ,
SIGTRAP ) = = NOTIFY_STOP ) {
return ;
}
2005-04-17 02:20:36 +04:00
if ( ( current - > ptrace & PT_PTRACED ) ! = 0 )
force_sig ( SIGTRAP , current ) ;
}
asmlinkage void
default_trap_handler ( struct pt_regs * regs , long interruption_code )
{
if ( regs - > psw . mask & PSW_MASK_PSTATE ) {
local_irq_enable ( ) ;
do_exit ( SIGSEGV ) ;
report_user_fault ( interruption_code , regs ) ;
} else
die ( " Unknown program exception " , regs , interruption_code ) ;
}
# define DO_ERROR_INFO(signr, str, name, sicode, siaddr) \
asmlinkage void name ( struct pt_regs * regs , long interruption_code ) \
{ \
siginfo_t info ; \
info . si_signo = signr ; \
info . si_errno = 0 ; \
info . si_code = sicode ; \
2006-07-12 18:41:55 +04:00
info . si_addr = siaddr ; \
2005-04-17 02:20:36 +04:00
do_trap ( interruption_code , signr , str , regs , & info ) ; \
}
DO_ERROR_INFO ( SIGILL , " addressing exception " , addressing_exception ,
ILL_ILLADR , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGILL , " execute exception " , execute_exception ,
ILL_ILLOPN , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGFPE , " fixpoint divide exception " , divide_exception ,
FPE_INTDIV , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGFPE , " fixpoint overflow exception " , overflow_exception ,
FPE_INTOVF , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGFPE , " HFP overflow exception " , hfp_overflow_exception ,
FPE_FLTOVF , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGFPE , " HFP underflow exception " , hfp_underflow_exception ,
FPE_FLTUND , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGFPE , " HFP significance exception " , hfp_significance_exception ,
FPE_FLTRES , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGFPE , " HFP divide exception " , hfp_divide_exception ,
FPE_FLTDIV , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGFPE , " HFP square root exception " , hfp_sqrt_exception ,
FPE_FLTINV , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGILL , " operand exception " , operand_exception ,
ILL_ILLOPN , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGILL , " privileged operation " , privileged_op ,
ILL_PRVOPC , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGILL , " special operation exception " , special_op_exception ,
ILL_ILLOPN , get_check_address ( regs ) )
DO_ERROR_INFO ( SIGILL , " translation exception " , translation_exception ,
ILL_ILLOPN , get_check_address ( regs ) )
static inline void
2006-07-12 18:41:55 +04:00
do_fp_trap ( struct pt_regs * regs , void __user * location ,
2005-04-17 02:20:36 +04:00
int fpc , long interruption_code )
{
siginfo_t si ;
si . si_signo = SIGFPE ;
si . si_errno = 0 ;
si . si_addr = location ;
si . si_code = 0 ;
/* 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 */
si . si_code = FPE_FLTINV ;
else if ( fpc & 0x4000 ) /* div by 0 */
si . si_code = FPE_FLTDIV ;
else if ( fpc & 0x2000 ) /* overflow */
si . si_code = FPE_FLTOVF ;
else if ( fpc & 0x1000 ) /* underflow */
si . si_code = FPE_FLTUND ;
else if ( fpc & 0x0800 ) /* inexact */
si . si_code = FPE_FLTRES ;
}
current - > thread . ieee_instruction_pointer = ( addr_t ) location ;
do_trap ( interruption_code , SIGFPE ,
" floating point exception " , regs , & si ) ;
}
asmlinkage void illegal_op ( struct pt_regs * regs , long interruption_code )
{
siginfo_t info ;
__u8 opcode [ 6 ] ;
2006-07-12 18:41:55 +04:00
__u16 __user * location ;
2005-04-17 02:20:36 +04:00
int signal = 0 ;
2006-07-12 18:41:55 +04:00
location = get_check_address ( regs ) ;
2005-04-17 02:20:36 +04:00
/*
* We got all needed information from the lowcore and can
* now safely switch on interrupts .
*/
if ( regs - > psw . mask & PSW_MASK_PSTATE )
local_irq_enable ( ) ;
if ( regs - > psw . mask & PSW_MASK_PSTATE ) {
2006-10-27 14:39:22 +04:00
if ( get_user ( * ( ( __u16 * ) opcode ) , ( __u16 __user * ) location ) )
return ;
2005-04-17 02:20:36 +04:00
if ( * ( ( __u16 * ) opcode ) = = S390_BREAKPOINT_U16 ) {
if ( current - > ptrace & PT_PTRACED )
force_sig ( SIGTRAP , current ) ;
else
signal = SIGILL ;
# ifdef CONFIG_MATHEMU
} else if ( opcode [ 0 ] = = 0xb3 ) {
2006-10-27 14:39:22 +04:00
if ( get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) )
return ;
2005-04-17 02:20:36 +04:00
signal = math_emu_b3 ( opcode , regs ) ;
} else if ( opcode [ 0 ] = = 0xed ) {
2006-10-27 14:39:22 +04:00
if ( get_user ( * ( ( __u32 * ) ( opcode + 2 ) ) ,
( __u32 __user * ) ( location + 1 ) ) )
return ;
2005-04-17 02:20:36 +04:00
signal = math_emu_ed ( opcode , regs ) ;
} else if ( * ( ( __u16 * ) opcode ) = = 0xb299 ) {
2006-10-27 14:39:22 +04:00
if ( get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) )
return ;
2005-04-17 02:20:36 +04:00
signal = math_emu_srnm ( opcode , regs ) ;
} else if ( * ( ( __u16 * ) opcode ) = = 0xb29c ) {
2006-10-27 14:39:22 +04:00
if ( get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) )
return ;
2005-04-17 02:20:36 +04:00
signal = math_emu_stfpc ( opcode , regs ) ;
} else if ( * ( ( __u16 * ) opcode ) = = 0xb29d ) {
2006-10-27 14:39:22 +04:00
if ( get_user ( * ( ( __u16 * ) ( opcode + 2 ) ) , location + 1 ) )
return ;
2005-04-17 02:20:36 +04:00
signal = math_emu_lfpc ( opcode , regs ) ;
# endif
} else
signal = SIGILL ;
} else
signal = SIGILL ;
# ifdef CONFIG_MATHEMU
if ( signal = = SIGFPE )
do_fp_trap ( regs , location ,
current - > thread . fp_regs . fpc , interruption_code ) ;
else if ( signal = = SIGSEGV ) {
info . si_signo = signal ;
info . si_errno = 0 ;
info . si_code = SEGV_MAPERR ;
2006-10-09 23:28:03 +04:00
info . si_addr = ( void __user * ) location ;
2005-04-17 02:20:36 +04:00
do_trap ( interruption_code , signal ,
" user address fault " , regs , & info ) ;
} else
# endif
if ( signal ) {
info . si_signo = signal ;
info . si_errno = 0 ;
info . si_code = ILL_ILLOPC ;
2006-02-01 14:55:59 +03:00
info . si_addr = ( void __user * ) location ;
2005-04-17 02:20:36 +04:00
do_trap ( interruption_code , signal ,
" illegal operation " , regs , & info ) ;
}
}
# ifdef CONFIG_MATHEMU
asmlinkage void
specification_exception ( struct pt_regs * regs , long interruption_code )
{
__u8 opcode [ 6 ] ;
2006-10-09 23:28:03 +04:00
__u16 __user * location = NULL ;
2005-04-17 02:20:36 +04:00
int signal = 0 ;
2006-10-09 23:28:03 +04:00
location = ( __u16 __user * ) get_check_address ( regs ) ;
2005-04-17 02:20:36 +04:00
/*
* We got all needed information from the lowcore and can
* now safely switch on interrupts .
*/
if ( regs - > psw . mask & PSW_MASK_PSTATE )
local_irq_enable ( ) ;
if ( regs - > psw . mask & PSW_MASK_PSTATE ) {
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 )
do_fp_trap ( regs , location ,
current - > thread . fp_regs . fpc , interruption_code ) ;
else if ( signal ) {
siginfo_t info ;
info . si_signo = signal ;
info . si_errno = 0 ;
info . si_code = ILL_ILLOPN ;
info . si_addr = location ;
do_trap ( interruption_code , signal ,
" specification exception " , regs , & info ) ;
}
}
# else
DO_ERROR_INFO ( SIGILL , " specification exception " , specification_exception ,
ILL_ILLOPN , get_check_address ( regs ) ) ;
# endif
asmlinkage void data_exception ( struct pt_regs * regs , long interruption_code )
{
2006-07-12 18:41:55 +04:00
__u16 __user * location ;
2005-04-17 02:20:36 +04:00
int signal = 0 ;
2006-07-12 18:41:55 +04:00
location = get_check_address ( regs ) ;
2005-04-17 02:20:36 +04:00
/*
* We got all needed information from the lowcore and can
* now safely switch on interrupts .
*/
if ( regs - > psw . mask & PSW_MASK_PSTATE )
local_irq_enable ( ) ;
if ( MACHINE_HAS_IEEE )
2006-09-28 18:56:43 +04:00
asm volatile ( " stfpc %0 " : " =m " ( current - > thread . fp_regs . fpc ) ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_MATHEMU
else if ( regs - > psw . mask & PSW_MASK_PSTATE ) {
__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 23:28:03 +04:00
( __u32 __user * ) ( location + 1 ) ) ;
2005-04-17 02:20:36 +04: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 )
do_fp_trap ( regs , location ,
current - > thread . fp_regs . fpc , interruption_code ) ;
else if ( signal ) {
siginfo_t info ;
info . si_signo = signal ;
info . si_errno = 0 ;
info . si_code = ILL_ILLOPN ;
info . si_addr = location ;
do_trap ( interruption_code , signal ,
" data exception " , regs , & info ) ;
}
}
asmlinkage void space_switch_exception ( struct pt_regs * regs , long int_code )
{
siginfo_t info ;
/* Set user psw back to home space mode. */
if ( regs - > psw . mask & PSW_MASK_PSTATE )
regs - > psw . mask | = PSW_ASC_HOME ;
/* Send SIGILL. */
info . si_signo = SIGILL ;
info . si_errno = 0 ;
info . si_code = ILL_PRVOPC ;
info . si_addr = get_check_address ( regs ) ;
do_trap ( int_code , SIGILL , " space switch event " , regs , & info ) ;
}
asmlinkage void kernel_stack_overflow ( struct pt_regs * regs )
{
2005-06-22 04:16:28 +04:00
bust_spinlocks ( 1 ) ;
printk ( " Kernel stack overflow. \n " ) ;
show_regs ( regs ) ;
bust_spinlocks ( 0 ) ;
2005-04-17 02:20:36 +04:00
panic ( " Corrupt kernel stack, can't continue. " ) ;
}
/* init is done in lowcore.S and head.S */
void __init trap_init ( void )
{
int i ;
for ( i = 0 ; i < 128 ; i + + )
pgm_check_table [ i ] = & default_trap_handler ;
pgm_check_table [ 1 ] = & illegal_op ;
pgm_check_table [ 2 ] = & privileged_op ;
pgm_check_table [ 3 ] = & execute_exception ;
pgm_check_table [ 4 ] = & do_protection_exception ;
pgm_check_table [ 5 ] = & addressing_exception ;
pgm_check_table [ 6 ] = & specification_exception ;
pgm_check_table [ 7 ] = & data_exception ;
pgm_check_table [ 8 ] = & overflow_exception ;
pgm_check_table [ 9 ] = & divide_exception ;
pgm_check_table [ 0x0A ] = & overflow_exception ;
pgm_check_table [ 0x0B ] = & divide_exception ;
pgm_check_table [ 0x0C ] = & hfp_overflow_exception ;
pgm_check_table [ 0x0D ] = & hfp_underflow_exception ;
pgm_check_table [ 0x0E ] = & hfp_significance_exception ;
pgm_check_table [ 0x0F ] = & hfp_divide_exception ;
pgm_check_table [ 0x10 ] = & do_dat_exception ;
pgm_check_table [ 0x11 ] = & do_dat_exception ;
pgm_check_table [ 0x12 ] = & translation_exception ;
pgm_check_table [ 0x13 ] = & special_op_exception ;
2006-01-06 11:19:28 +03:00
# ifdef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
pgm_check_table [ 0x38 ] = & do_dat_exception ;
pgm_check_table [ 0x39 ] = & do_dat_exception ;
pgm_check_table [ 0x3A ] = & do_dat_exception ;
pgm_check_table [ 0x3B ] = & do_dat_exception ;
2006-01-06 11:19:28 +03:00
# endif /* CONFIG_64BIT */
2005-04-17 02:20:36 +04:00
pgm_check_table [ 0x15 ] = & operand_exception ;
pgm_check_table [ 0x1C ] = & space_switch_exception ;
pgm_check_table [ 0x1D ] = & hfp_sqrt_exception ;
pgm_check_table [ 0x40 ] = & do_monitor_call ;
if ( MACHINE_IS_VM ) {
2005-11-07 11:59:06 +03:00
# ifdef CONFIG_PFAULT
2005-04-17 02:20:36 +04:00
/*
2005-11-07 11:59:06 +03:00
* Try to get pfault pseudo page faults going .
2005-04-17 02:20:36 +04:00
*/
if ( register_early_external_interrupt ( 0x2603 , pfault_interrupt ,
& ext_int_pfault ) ! = 0 )
panic ( " Couldn't request external interrupt 0x2603 " ) ;
if ( pfault_init ( ) = = 0 )
return ;
/* Tough luck, no pfault. */
unregister_early_external_interrupt ( 0x2603 , pfault_interrupt ,
& ext_int_pfault ) ;
# endif
}
}