2006-10-12 12:07:45 +04:00
/*
* ' traps . c ' handles hardware traps and faults after we have saved some
* state in ' entry . S ' .
2005-04-17 02:20:36 +04:00
*
* SuperH version : Copyright ( C ) 1999 Niibe Yutaka
* Copyright ( C ) 2000 Philipp Rumpf
* Copyright ( C ) 2000 David Howells
2010-10-13 01:55:26 +04:00
* Copyright ( C ) 2002 - 2010 Paul Mundt
2006-10-12 12:07:45 +04:00
*
* 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 .
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/ptrace.h>
2009-01-07 01:41:07 +03:00
# include <linux/hardirq.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/kallsyms.h>
2006-10-19 11:20:25 +04:00
# include <linux/io.h>
2007-03-08 13:41:21 +03:00
# include <linux/bug.h>
2006-12-06 05:07:51 +03:00
# include <linux/debug_locks.h>
2007-05-09 05:55:38 +04:00
# include <linux/kdebug.h>
2006-12-08 11:41:43 +03:00
# include <linux/limits.h>
2009-10-13 05:57:52 +04:00
# include <linux/sysfs.h>
2010-01-12 10:12:25 +03:00
# include <linux/uaccess.h>
2010-10-13 01:55:26 +04:00
# include <linux/perf_event.h>
2017-02-08 20:51:37 +03:00
# include <linux/sched/task_stack.h>
2010-01-12 10:12:25 +03:00
# include <asm/alignment.h>
2008-04-15 21:03:51 +04:00
# include <asm/fpu.h>
2008-09-05 12:15:39 +04:00
# include <asm/kprobes.h>
2012-03-28 21:30:03 +04:00
# include <asm/traps.h>
# include <asm/bl_bit.h>
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_CPU_SH2
2006-11-05 09:58:47 +03:00
# define TRAP_RESERVED_INST 4
# define TRAP_ILLEGAL_SLOT_INST 6
# define TRAP_ADDRESS_ERROR 9
# ifdef CONFIG_CPU_SH2A
2009-05-08 18:51:51 +04:00
# define TRAP_UBC 12
2008-07-09 20:20:03 +04:00
# define TRAP_FPU_ERROR 13
2006-11-05 09:58:47 +03:00
# define TRAP_DIVZERO_ERROR 17
# define TRAP_DIVOVF_ERROR 18
# endif
2005-04-17 02:20:36 +04:00
# else
# define TRAP_RESERVED_INST 12
# define TRAP_ILLEGAL_SLOT_INST 13
# endif
2008-02-06 18:02:50 +03:00
static inline void sign_extend ( unsigned int count , unsigned char * dst )
{
# ifdef __LITTLE_ENDIAN__
2008-02-07 13:58:46 +03:00
if ( ( count = = 1 ) & & dst [ 0 ] & 0x80 ) {
dst [ 1 ] = 0xff ;
dst [ 2 ] = 0xff ;
dst [ 3 ] = 0xff ;
}
2008-02-06 18:02:50 +03:00
if ( ( count = = 2 ) & & dst [ 1 ] & 0x80 ) {
dst [ 2 ] = 0xff ;
dst [ 3 ] = 0xff ;
}
# else
2008-02-07 13:58:46 +03:00
if ( ( count = = 1 ) & & dst [ 3 ] & 0x80 ) {
dst [ 2 ] = 0xff ;
dst [ 1 ] = 0xff ;
2008-02-06 18:02:50 +03:00
dst [ 0 ] = 0xff ;
2008-02-07 13:58:46 +03:00
}
if ( ( count = = 2 ) & & dst [ 2 ] & 0x80 ) {
2008-02-06 18:02:50 +03:00
dst [ 1 ] = 0xff ;
2008-02-07 13:58:46 +03:00
dst [ 0 ] = 0xff ;
2008-02-06 18:02:50 +03:00
}
# endif
}
2008-02-07 14:18:21 +03:00
static struct mem_access user_mem_access = {
copy_from_user ,
copy_to_user ,
} ;
2005-04-17 02:20:36 +04:00
/*
* handle an instruction that does an unaligned memory access by emulating the
* desired behaviour
* - note that PC _may not_ point to the faulting instruction
* ( if that instruction is in a branch delay slot )
* - return 0 if emulation okay , - EFAULT on existential error
*/
2009-05-09 11:02:08 +04:00
static int handle_unaligned_ins ( insn_size_t instruction , struct pt_regs * regs ,
2008-02-07 14:18:21 +03:00
struct mem_access * ma )
2005-04-17 02:20:36 +04:00
{
int ret , index , count ;
unsigned long * rm , * rn ;
unsigned char * src , * dst ;
2008-09-04 13:53:58 +04:00
unsigned char __user * srcu , * dstu ;
2005-04-17 02:20:36 +04:00
index = ( instruction > > 8 ) & 15 ; /* 0x0F00 */
rn = & regs - > regs [ index ] ;
index = ( instruction > > 4 ) & 15 ; /* 0x00F0 */
rm = & regs - > regs [ index ] ;
count = 1 < < ( instruction & 3 ) ;
2009-08-24 09:53:46 +04:00
switch ( count ) {
2010-01-12 10:12:25 +03:00
case 1 : inc_unaligned_byte_access ( ) ; break ;
case 2 : inc_unaligned_word_access ( ) ; break ;
case 4 : inc_unaligned_dword_access ( ) ; break ;
case 8 : inc_unaligned_multi_access ( ) ; break ;
2009-08-24 09:53:46 +04:00
}
2005-04-17 02:20:36 +04:00
ret = - EFAULT ;
switch ( instruction > > 12 ) {
case 0 : /* mov.[bwl] to/from memory via r0+rn */
if ( instruction & 8 ) {
/* from memory */
2008-09-04 13:53:58 +04:00
srcu = ( unsigned char __user * ) * rm ;
srcu + = regs - > regs [ 0 ] ;
dst = ( unsigned char * ) rn ;
* ( unsigned long * ) dst = 0 ;
2005-04-17 02:20:36 +04:00
2008-02-06 18:02:50 +03:00
# if !defined(__LITTLE_ENDIAN__)
2005-04-17 02:20:36 +04:00
dst + = 4 - count ;
2008-02-06 18:02:50 +03:00
# endif
2008-09-04 13:53:58 +04:00
if ( ma - > from ( dst , srcu , count ) )
2005-04-17 02:20:36 +04:00
goto fetch_fault ;
2008-02-06 18:02:50 +03:00
sign_extend ( count , dst ) ;
2005-04-17 02:20:36 +04:00
} else {
/* to memory */
2008-09-04 13:53:58 +04:00
src = ( unsigned char * ) rm ;
2005-04-17 02:20:36 +04:00
# if !defined(__LITTLE_ENDIAN__)
src + = 4 - count ;
# endif
2008-09-04 13:53:58 +04:00
dstu = ( unsigned char __user * ) * rn ;
dstu + = regs - > regs [ 0 ] ;
2005-04-17 02:20:36 +04:00
2008-09-04 13:53:58 +04:00
if ( ma - > to ( dstu , src , count ) )
2005-04-17 02:20:36 +04:00
goto fetch_fault ;
}
ret = 0 ;
break ;
case 1 : /* mov.l Rm,@(disp,Rn) */
src = ( unsigned char * ) rm ;
2008-09-04 13:53:58 +04:00
dstu = ( unsigned char __user * ) * rn ;
dstu + = ( instruction & 0x000F ) < < 2 ;
2005-04-17 02:20:36 +04:00
2008-09-04 13:53:58 +04:00
if ( ma - > to ( dstu , src , 4 ) )
2005-04-17 02:20:36 +04:00
goto fetch_fault ;
ret = 0 ;
2006-11-21 07:34:04 +03:00
break ;
2005-04-17 02:20:36 +04:00
case 2 : /* mov.[bwl] to memory, possibly with pre-decrement */
if ( instruction & 4 )
* rn - = count ;
src = ( unsigned char * ) rm ;
2008-09-04 13:53:58 +04:00
dstu = ( unsigned char __user * ) * rn ;
2005-04-17 02:20:36 +04:00
# if !defined(__LITTLE_ENDIAN__)
src + = 4 - count ;
# endif
2008-09-04 13:53:58 +04:00
if ( ma - > to ( dstu , src , count ) )
2005-04-17 02:20:36 +04:00
goto fetch_fault ;
ret = 0 ;
break ;
case 5 : /* mov.l @(disp,Rm),Rn */
2008-09-04 13:53:58 +04:00
srcu = ( unsigned char __user * ) * rm ;
srcu + = ( instruction & 0x000F ) < < 2 ;
dst = ( unsigned char * ) rn ;
* ( unsigned long * ) dst = 0 ;
2005-04-17 02:20:36 +04:00
2008-09-04 13:53:58 +04:00
if ( ma - > from ( dst , srcu , 4 ) )
2005-04-17 02:20:36 +04:00
goto fetch_fault ;
ret = 0 ;
2006-11-21 07:34:04 +03:00
break ;
2005-04-17 02:20:36 +04:00
case 6 : /* mov.[bwl] from memory, possibly with post-increment */
2008-09-04 13:53:58 +04:00
srcu = ( unsigned char __user * ) * rm ;
2005-04-17 02:20:36 +04:00
if ( instruction & 4 )
* rm + = count ;
dst = ( unsigned char * ) rn ;
* ( unsigned long * ) dst = 0 ;
2006-11-21 07:34:04 +03:00
2008-02-06 18:02:50 +03:00
# if !defined(__LITTLE_ENDIAN__)
2005-04-17 02:20:36 +04:00
dst + = 4 - count ;
2008-02-06 18:02:50 +03:00
# endif
2008-09-04 13:53:58 +04:00
if ( ma - > from ( dst , srcu , count ) )
2005-04-17 02:20:36 +04:00
goto fetch_fault ;
2008-02-06 18:02:50 +03:00
sign_extend ( count , dst ) ;
2005-04-17 02:20:36 +04:00
ret = 0 ;
break ;
case 8 :
switch ( ( instruction & 0xFF00 ) > > 8 ) {
case 0x81 : /* mov.w R0,@(disp,Rn) */
2008-09-04 13:53:58 +04:00
src = ( unsigned char * ) & regs - > regs [ 0 ] ;
2005-04-17 02:20:36 +04:00
# if !defined(__LITTLE_ENDIAN__)
src + = 2 ;
# endif
2008-09-04 13:53:58 +04:00
dstu = ( unsigned char __user * ) * rm ; /* called Rn in the spec */
dstu + = ( instruction & 0x000F ) < < 1 ;
2005-04-17 02:20:36 +04:00
2008-09-04 13:53:58 +04:00
if ( ma - > to ( dstu , src , 2 ) )
2005-04-17 02:20:36 +04:00
goto fetch_fault ;
ret = 0 ;
break ;
case 0x85 : /* mov.w @(disp,Rm),R0 */
2008-09-04 13:53:58 +04:00
srcu = ( unsigned char __user * ) * rm ;
srcu + = ( instruction & 0x000F ) < < 1 ;
dst = ( unsigned char * ) & regs - > regs [ 0 ] ;
* ( unsigned long * ) dst = 0 ;
2005-04-17 02:20:36 +04:00
# if !defined(__LITTLE_ENDIAN__)
dst + = 2 ;
# endif
2008-09-04 13:53:58 +04:00
if ( ma - > from ( dst , srcu , 2 ) )
2005-04-17 02:20:36 +04:00
goto fetch_fault ;
2008-02-06 18:02:50 +03:00
sign_extend ( 2 , dst ) ;
2005-04-17 02:20:36 +04:00
ret = 0 ;
break ;
}
break ;
2011-08-24 14:43:59 +04:00
case 9 : /* mov.w @(disp,PC),Rn */
srcu = ( unsigned char __user * ) regs - > pc ;
srcu + = 4 ;
srcu + = ( instruction & 0x00FF ) < < 1 ;
dst = ( unsigned char * ) rn ;
* ( unsigned long * ) dst = 0 ;
# if !defined(__LITTLE_ENDIAN__)
dst + = 2 ;
# endif
if ( ma - > from ( dst , srcu , 2 ) )
goto fetch_fault ;
sign_extend ( 2 , dst ) ;
ret = 0 ;
break ;
case 0xd : /* mov.l @(disp,PC),Rn */
srcu = ( unsigned char __user * ) ( regs - > pc & ~ 0x3 ) ;
srcu + = 4 ;
srcu + = ( instruction & 0x00FF ) < < 2 ;
dst = ( unsigned char * ) rn ;
* ( unsigned long * ) dst = 0 ;
if ( ma - > from ( dst , srcu , 4 ) )
goto fetch_fault ;
ret = 0 ;
break ;
2005-04-17 02:20:36 +04:00
}
return ret ;
fetch_fault :
/* Argh. Address not only misaligned but also non-existent.
* Raise an EFAULT and see if it ' s trapped
*/
2009-01-21 03:42:10 +03:00
die_if_no_fixup ( " Fault in unaligned fixup " , regs , 0 ) ;
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}
/*
* emulate the instruction in the delay slot
* - fetches the instruction from PC + 2
*/
2008-02-07 14:18:21 +03:00
static inline int handle_delayslot ( struct pt_regs * regs ,
2009-05-09 11:02:08 +04:00
insn_size_t old_instruction ,
2008-02-07 14:18:21 +03:00
struct mem_access * ma )
2005-04-17 02:20:36 +04:00
{
2009-05-09 11:02:08 +04:00
insn_size_t instruction ;
2008-09-04 13:53:58 +04:00
void __user * addr = ( void __user * ) ( regs - > pc +
instruction_size ( old_instruction ) ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 14:04:12 +03:00
if ( copy_from_user ( & instruction , addr , sizeof ( instruction ) ) ) {
2005-04-17 02:20:36 +04:00
/* the instruction-fetch faulted */
if ( user_mode ( regs ) )
return - EFAULT ;
/* kernel */
2006-11-21 07:34:04 +03:00
die ( " delay-slot-insn faulting in handle_unaligned_delayslot " ,
regs , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 14:18:21 +03:00
return handle_unaligned_ins ( instruction , regs , ma ) ;
2005-04-17 02:20:36 +04:00
}
/*
* handle an instruction that does an unaligned memory access
* - have to be careful of branch delay - slot instructions that fault
* SH3 :
* - if the branch would be taken PC points to the branch
* - if the branch would not be taken , PC points to delay - slot
* SH4 :
* - PC always points to delayed branch
* - return 0 if handled , - EFAULT if failed ( may not return if in kernel )
*/
/* Macros to determine offset from current PC for branch instructions */
/* Explicit type coercion is used to force sign extension where needed */
# define SH_PC_8BIT_OFFSET(instr) ((((signed char)(instr))*2) + 4)
# define SH_PC_12BIT_OFFSET(instr) ((((signed short)(instr<<4))>>3) + 4)
2009-05-09 11:02:08 +04:00
int handle_unaligned_access ( insn_size_t instruction , struct pt_regs * regs ,
2010-10-13 01:55:26 +04:00
struct mem_access * ma , int expected ,
unsigned long address )
2005-04-17 02:20:36 +04:00
{
u_int rm ;
int ret , index ;
2009-09-24 12:38:18 +04:00
/*
* XXX : We can ' t handle mixed 16 / 32 - bit instructions yet
*/
if ( instruction_size ( instruction ) ! = 2 )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
index = ( instruction > > 8 ) & 15 ; /* 0x0F00 */
rm = regs - > regs [ index ] ;
2010-10-13 01:55:26 +04:00
/*
* Log the unexpected fixups , and then pass them on to perf .
*
* We intentionally don ' t report the expected cases to perf as
* otherwise the trapped I / O case will skew the results too much
* to be useful .
*/
if ( ! expected ) {
2010-01-12 10:12:25 +03:00
unaligned_fixups_notify ( current , instruction , regs ) ;
2011-06-27 16:41:57 +04:00
perf_sw_event ( PERF_COUNT_SW_ALIGNMENT_FAULTS , 1 ,
2010-10-13 01:55:26 +04:00
regs , address ) ;
}
2005-04-17 02:20:36 +04:00
ret = - EFAULT ;
switch ( instruction & 0xF000 ) {
case 0x0000 :
if ( instruction = = 0x000B ) {
/* rts */
2008-02-07 14:18:21 +03:00
ret = handle_delayslot ( regs , instruction , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 )
regs - > pc = regs - > pr ;
}
else if ( ( instruction & 0x00FF ) = = 0x0023 ) {
/* braf @Rm */
2008-02-07 14:18:21 +03:00
ret = handle_delayslot ( regs , instruction , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 )
regs - > pc + = rm + 4 ;
}
else if ( ( instruction & 0x00FF ) = = 0x0003 ) {
/* bsrf @Rm */
2008-02-07 14:18:21 +03:00
ret = handle_delayslot ( regs , instruction , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 ) {
regs - > pr = regs - > pc + 4 ;
regs - > pc + = rm + 4 ;
}
}
else {
/* mov.[bwl] to/from memory via r0+rn */
goto simple ;
}
break ;
case 0x1000 : /* mov.l Rm,@(disp,Rn) */
goto simple ;
case 0x2000 : /* mov.[bwl] to memory, possibly with pre-decrement */
goto simple ;
case 0x4000 :
if ( ( instruction & 0x00FF ) = = 0x002B ) {
/* jmp @Rm */
2008-02-07 14:18:21 +03:00
ret = handle_delayslot ( regs , instruction , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 )
regs - > pc = rm ;
}
else if ( ( instruction & 0x00FF ) = = 0x000B ) {
/* jsr @Rm */
2008-02-07 14:18:21 +03:00
ret = handle_delayslot ( regs , instruction , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 ) {
regs - > pr = regs - > pc + 4 ;
regs - > pc = rm ;
}
}
else {
/* mov.[bwl] to/from memory via r0+rn */
goto simple ;
}
break ;
case 0x5000 : /* mov.l @(disp,Rm),Rn */
goto simple ;
case 0x6000 : /* mov.[bwl] from memory, possibly with post-increment */
goto simple ;
case 0x8000 : /* bf lab, bf/s lab, bt lab, bt/s lab */
switch ( instruction & 0x0F00 ) {
case 0x0100 : /* mov.w R0,@(disp,Rm) */
goto simple ;
case 0x0500 : /* mov.w @(disp,Rm),R0 */
goto simple ;
case 0x0B00 : /* bf lab - no delayslot*/
2011-08-22 19:56:08 +04:00
ret = 0 ;
2005-04-17 02:20:36 +04:00
break ;
case 0x0F00 : /* bf/s lab */
2008-02-07 14:18:21 +03:00
ret = handle_delayslot ( regs , instruction , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 ) {
# if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
if ( ( regs - > sr & 0x00000001 ) ! = 0 )
regs - > pc + = 4 ; /* next after slot */
else
# endif
regs - > pc + = SH_PC_8BIT_OFFSET ( instruction ) ;
}
break ;
case 0x0900 : /* bt lab - no delayslot */
2011-08-22 19:56:08 +04:00
ret = 0 ;
2005-04-17 02:20:36 +04:00
break ;
case 0x0D00 : /* bt/s lab */
2008-02-07 14:18:21 +03:00
ret = handle_delayslot ( regs , instruction , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 ) {
# if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
if ( ( regs - > sr & 0x00000001 ) = = 0 )
regs - > pc + = 4 ; /* next after slot */
else
# endif
regs - > pc + = SH_PC_8BIT_OFFSET ( instruction ) ;
}
break ;
}
break ;
2011-08-24 14:43:59 +04:00
case 0x9000 : /* mov.w @(disp,Rm),Rn */
goto simple ;
2005-04-17 02:20:36 +04:00
case 0xA000 : /* bra label */
2008-02-07 14:18:21 +03:00
ret = handle_delayslot ( regs , instruction , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 )
regs - > pc + = SH_PC_12BIT_OFFSET ( instruction ) ;
break ;
case 0xB000 : /* bsr label */
2008-02-07 14:18:21 +03:00
ret = handle_delayslot ( regs , instruction , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 ) {
regs - > pr = regs - > pc + 4 ;
regs - > pc + = SH_PC_12BIT_OFFSET ( instruction ) ;
}
break ;
2011-08-24 14:43:59 +04:00
case 0xD000 : /* mov.l @(disp,Rm),Rn */
goto simple ;
2005-04-17 02:20:36 +04:00
}
return ret ;
/* handle non-delay-slot instruction */
simple :
2008-02-07 14:18:21 +03:00
ret = handle_unaligned_ins ( instruction , regs , ma ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 )
2007-05-08 10:31:48 +04:00
regs - > pc + = instruction_size ( instruction ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
/*
2006-11-21 07:34:04 +03:00
* Handle various address error exceptions :
* - instruction address error :
* misaligned PC
* PC > = 0x80000000 in user mode
* - data address error ( read and write )
* misaligned data access
* access to > = 0x80000000 is user mode
* Unfortuntaly we can ' t distinguish between instruction address error
2007-05-14 03:15:10 +04:00
* and data address errors caused by read accesses .
2005-04-17 02:20:36 +04:00
*/
2006-11-21 05:16:57 +03:00
asmlinkage void do_address_error ( struct pt_regs * regs ,
2005-04-17 02:20:36 +04:00
unsigned long writeaccess ,
unsigned long address )
{
2006-11-05 09:58:47 +03:00
unsigned long error_code = 0 ;
2005-04-17 02:20:36 +04:00
mm_segment_t oldfs ;
2009-05-09 11:02:08 +04:00
insn_size_t instruction ;
2005-04-17 02:20:36 +04:00
int tmp ;
2006-11-05 09:58:47 +03:00
/* Intentional ifdef */
# ifdef CONFIG_CPU_HAS_SR_RB
2008-09-21 07:00:23 +04:00
error_code = lookup_exception_vector ( ) ;
2006-11-05 09:58:47 +03:00
# endif
2005-04-17 02:20:36 +04:00
oldfs = get_fs ( ) ;
if ( user_mode ( regs ) ) {
2006-11-21 07:34:04 +03:00
int si_code = BUS_ADRERR ;
2010-01-12 10:12:25 +03:00
unsigned int user_action ;
2006-11-21 07:34:04 +03:00
2005-04-17 02:20:36 +04:00
local_irq_enable ( ) ;
2010-01-12 10:12:25 +03:00
inc_unaligned_user_access ( ) ;
2009-08-24 09:53:46 +04:00
2009-08-24 10:01:10 +04:00
set_fs ( USER_DS ) ;
2009-09-24 12:38:18 +04:00
if ( copy_from_user ( & instruction , ( insn_size_t * ) ( regs - > pc & ~ 1 ) ,
sizeof ( instruction ) ) ) {
2009-08-24 10:01:10 +04:00
set_fs ( oldfs ) ;
goto uspace_segv ;
}
set_fs ( oldfs ) ;
2009-08-24 09:53:46 +04:00
/* shout about userspace fixups */
2010-01-12 10:12:25 +03:00
unaligned_fixups_notify ( current , instruction , regs ) ;
2009-08-24 09:53:46 +04:00
2010-01-12 10:12:25 +03:00
user_action = unaligned_user_action ( ) ;
if ( user_action & UM_FIXUP )
2009-08-24 09:53:46 +04:00
goto fixup ;
2010-01-12 10:12:25 +03:00
if ( user_action & UM_SIGNAL )
2009-08-24 09:53:46 +04:00
goto uspace_segv ;
else {
/* ignore */
2009-08-24 10:01:10 +04:00
regs - > pc + = instruction_size ( instruction ) ;
2009-08-24 09:53:46 +04:00
return ;
}
fixup :
2005-04-17 02:20:36 +04:00
/* bad PC is not something we can fix */
2006-11-21 07:34:04 +03:00
if ( regs - > pc & 1 ) {
si_code = BUS_ADRALN ;
2005-04-17 02:20:36 +04:00
goto uspace_segv ;
2006-11-21 07:34:04 +03:00
}
2005-04-17 02:20:36 +04:00
set_fs ( USER_DS ) ;
2008-02-07 14:18:21 +03:00
tmp = handle_unaligned_access ( instruction , regs ,
2010-10-13 01:55:26 +04:00
& user_mem_access , 0 ,
address ) ;
2005-04-17 02:20:36 +04:00
set_fs ( oldfs ) ;
2010-01-12 10:12:25 +03:00
if ( tmp = = 0 )
2005-04-17 02:20:36 +04:00
return ; /* sorted */
2006-11-21 07:34:04 +03:00
uspace_segv :
printk ( KERN_NOTICE " Sending SIGBUS to \" %s \" due to unaligned "
" access (PC %lx PR %lx) \n " , current - > comm , regs - > pc ,
regs - > pr ) ;
2018-04-16 03:56:33 +03:00
force_sig_fault ( SIGBUS , si_code , ( void __user * ) address , current ) ;
2005-04-17 02:20:36 +04:00
} else {
2010-01-12 10:12:25 +03:00
inc_unaligned_kernel_access ( ) ;
2009-08-24 09:53:46 +04:00
2005-04-17 02:20:36 +04:00
if ( regs - > pc & 1 )
die ( " unaligned program counter " , regs , error_code ) ;
set_fs ( KERNEL_DS ) ;
2008-09-04 13:53:58 +04:00
if ( copy_from_user ( & instruction , ( void __user * ) ( regs - > pc ) ,
2008-02-07 14:04:12 +03:00
sizeof ( instruction ) ) ) {
2005-04-17 02:20:36 +04:00
/* Argh. Fault on the instruction itself.
This should never happen non - SMP
*/
set_fs ( oldfs ) ;
die ( " insn faulting in do_address_error " , regs , 0 ) ;
}
2010-01-12 10:12:25 +03:00
unaligned_fixups_notify ( current , instruction , regs ) ;
2009-09-24 12:48:15 +04:00
2010-10-13 01:55:26 +04:00
handle_unaligned_access ( instruction , regs , & user_mem_access ,
0 , address ) ;
2005-04-17 02:20:36 +04:00
set_fs ( oldfs ) ;
}
}
# ifdef CONFIG_SH_DSP
/*
* SH - DSP support gerg @ snapgear . com .
*/
int is_dsp_inst ( struct pt_regs * regs )
{
2007-05-14 12:26:34 +04:00
unsigned short inst = 0 ;
2005-04-17 02:20:36 +04:00
2006-11-21 05:16:57 +03:00
/*
2005-04-17 02:20:36 +04:00
* Safe guard if DSP mode is already enabled or we ' re lacking
* the DSP altogether .
*/
2006-12-25 04:19:56 +03:00
if ( ! ( current_cpu_data . flags & CPU_HAS_DSP ) | | ( regs - > sr & SR_DSP ) )
2005-04-17 02:20:36 +04:00
return 0 ;
get_user ( inst , ( ( unsigned short * ) regs - > pc ) ) ;
inst & = 0xf000 ;
/* Check for any type of DSP or support instruction */
if ( ( inst = = 0xf000 ) | | ( inst = = 0x4000 ) )
return 1 ;
return 0 ;
}
# else
# define is_dsp_inst(regs) (0)
# endif /* CONFIG_SH_DSP */
2006-11-05 09:58:47 +03:00
# ifdef CONFIG_CPU_SH2A
2014-04-04 01:46:41 +04:00
asmlinkage void do_divide_error ( unsigned long r4 )
2006-11-05 09:58:47 +03:00
{
2018-04-16 03:56:33 +03:00
int code ;
2006-11-05 09:58:47 +03:00
switch ( r4 ) {
case TRAP_DIVZERO_ERROR :
2018-04-16 03:56:33 +03:00
code = FPE_INTDIV ;
2006-11-05 09:58:47 +03:00
break ;
case TRAP_DIVOVF_ERROR :
2018-04-16 03:56:33 +03:00
code = FPE_INTOVF ;
2006-11-05 09:58:47 +03:00
break ;
2018-05-29 17:40:11 +03:00
default :
/* Let gcc know unhandled cases don't make it past here */
return ;
2006-11-05 09:58:47 +03:00
}
2018-04-16 03:56:33 +03:00
force_sig_fault ( SIGFPE , code , NULL , current ) ;
2006-11-05 09:58:47 +03:00
}
# endif
2014-04-04 01:46:41 +04:00
asmlinkage void do_reserved_inst ( void )
2006-09-27 12:15:32 +04:00
{
2014-04-04 01:46:41 +04:00
struct pt_regs * regs = current_pt_regs ( ) ;
2006-09-27 12:15:32 +04:00
unsigned long error_code ;
struct task_struct * tsk = current ;
# ifdef CONFIG_SH_FPU_EMU
2006-11-05 09:58:47 +03:00
unsigned short inst = 0 ;
2006-09-27 12:15:32 +04:00
int err ;
2006-11-21 05:16:57 +03:00
get_user ( inst , ( unsigned short * ) regs - > pc ) ;
2006-09-27 12:15:32 +04:00
2006-11-21 05:16:57 +03:00
err = do_fpu_inst ( inst , regs ) ;
2006-09-27 12:15:32 +04:00
if ( ! err ) {
2007-05-08 10:31:48 +04:00
regs - > pc + = instruction_size ( inst ) ;
2006-09-27 12:15:32 +04:00
return ;
}
/* not a FPU inst. */
# endif
# ifdef CONFIG_SH_DSP
/* Check if it's a DSP instruction */
2006-11-21 07:34:04 +03:00
if ( is_dsp_inst ( regs ) ) {
2006-09-27 12:15:32 +04:00
/* Enable DSP mode, and restart instruction. */
2006-11-21 05:16:57 +03:00
regs - > sr | = SR_DSP ;
2009-04-03 21:32:33 +04:00
/* Save DSP mode */
tsk - > thread . dsp_status . status | = SR_DSP ;
2006-09-27 12:15:32 +04:00
return ;
}
# endif
2008-09-21 07:00:23 +04:00
error_code = lookup_exception_vector ( ) ;
2006-11-05 09:58:47 +03:00
2006-09-27 12:15:32 +04:00
local_irq_enable ( ) ;
force_sig ( SIGILL , tsk ) ;
2006-11-21 05:16:57 +03:00
die_if_no_fixup ( " reserved instruction " , regs , error_code ) ;
2006-09-27 12:15:32 +04:00
}
# ifdef CONFIG_SH_FPU_EMU
2008-11-26 07:06:04 +03:00
static int emulate_branch ( unsigned short inst , struct pt_regs * regs )
2006-09-27 12:15:32 +04:00
{
/*
* bfs : 8f xx : PC + = d * 2 + 4 ;
* bts : 8 dxx : PC + = d * 2 + 4 ;
* bra : axxx : PC + = D * 2 + 4 ;
* bsr : bxxx : PC + = D * 2 + 4 after PR = PC + 4 ;
* braf : 0x23 : PC + = Rn * 2 + 4 ;
* bsrf : 0x03 : PC + = Rn * 2 + 4 after PR = PC + 4 ;
* jmp : 4 x2b : PC = Rn ;
* jsr : 4 x0b : PC = Rn after PR = PC + 4 ;
* rts : 000 b : PC = PR ;
*/
2008-11-26 07:06:04 +03:00
if ( ( ( inst & 0xf000 ) = = 0xb000 ) | | /* bsr */
( ( inst & 0xf0ff ) = = 0x0003 ) | | /* bsrf */
( ( inst & 0xf0ff ) = = 0x400b ) ) /* jsr */
regs - > pr = regs - > pc + 4 ;
if ( ( inst & 0xfd00 ) = = 0x8d00 ) { /* bfs, bts */
2006-09-27 12:15:32 +04:00
regs - > pc + = SH_PC_8BIT_OFFSET ( inst ) ;
return 0 ;
}
2008-11-26 07:06:04 +03:00
if ( ( inst & 0xe000 ) = = 0xa000 ) { /* bra, bsr */
2006-09-27 12:15:32 +04:00
regs - > pc + = SH_PC_12BIT_OFFSET ( inst ) ;
return 0 ;
}
2008-11-26 07:06:04 +03:00
if ( ( inst & 0xf0df ) = = 0x0003 ) { /* braf, bsrf */
2006-09-27 12:15:32 +04:00
regs - > pc + = regs - > regs [ ( inst & 0x0f00 ) > > 8 ] + 4 ;
return 0 ;
}
2008-11-26 07:06:04 +03:00
if ( ( inst & 0xf0df ) = = 0x400b ) { /* jmp, jsr */
2006-09-27 12:15:32 +04:00
regs - > pc = regs - > regs [ ( inst & 0x0f00 ) > > 8 ] ;
return 0 ;
}
2008-11-26 07:06:04 +03:00
if ( ( inst & 0xffff ) = = 0x000b ) { /* rts */
2006-09-27 12:15:32 +04:00
regs - > pc = regs - > pr ;
return 0 ;
}
return 1 ;
}
# endif
2014-04-04 01:46:41 +04:00
asmlinkage void do_illegal_slot_inst ( void )
2006-09-27 12:15:32 +04:00
{
2014-04-04 01:46:41 +04:00
struct pt_regs * regs = current_pt_regs ( ) ;
2008-09-17 18:12:11 +04:00
unsigned long inst ;
2006-09-27 12:15:32 +04:00
struct task_struct * tsk = current ;
2008-09-05 12:15:39 +04:00
if ( kprobe_handle_illslot ( regs - > pc ) = = 0 )
return ;
2006-09-27 12:15:32 +04:00
# ifdef CONFIG_SH_FPU_EMU
2006-11-21 05:16:57 +03:00
get_user ( inst , ( unsigned short * ) regs - > pc + 1 ) ;
if ( ! do_fpu_inst ( inst , regs ) ) {
get_user ( inst , ( unsigned short * ) regs - > pc ) ;
if ( ! emulate_branch ( inst , regs ) )
2006-09-27 12:15:32 +04:00
return ;
/* fault in branch.*/
}
/* not a FPU inst. */
# endif
2008-09-21 07:00:23 +04:00
inst = lookup_exception_vector ( ) ;
2006-11-05 09:58:47 +03:00
2006-09-27 12:15:32 +04:00
local_irq_enable ( ) ;
force_sig ( SIGILL , tsk ) ;
2008-09-17 18:12:11 +04:00
die_if_no_fixup ( " illegal slot instruction " , regs , inst ) ;
2006-09-27 12:15:32 +04:00
}
2005-04-17 02:20:36 +04:00
2014-04-04 01:46:41 +04:00
asmlinkage void do_exception_error ( void )
2005-04-17 02:20:36 +04:00
{
long ex ;
2006-11-05 09:58:47 +03:00
2008-09-21 07:00:23 +04:00
ex = lookup_exception_vector ( ) ;
2014-04-04 01:46:41 +04:00
die_if_kernel ( " exception " , current_pt_regs ( ) , ex ) ;
2005-04-17 02:20:36 +04:00
}
2013-06-19 01:10:12 +04:00
void per_cpu_trap_init ( void )
2005-04-17 02:20:36 +04:00
{
extern void * vbr_base ;
/* NOTE: The VBR value should be at P1
( or P2 , virtural " fixed " address space ) .
It ' s definitely should not in physical address . */
asm volatile ( " ldc %0, vbr "
: /* no output */
: " r " ( & vbr_base )
: " memory " ) ;
2010-09-24 13:05:38 +04:00
/* disable exception blocking now when the vbr has been setup */
clear_bl_bit ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-19 11:20:25 +04:00
void * set_exception_table_vec ( unsigned int vec , void * handler )
2005-04-17 02:20:36 +04:00
{
extern void * exception_handling_table [ ] ;
2006-10-19 11:20:25 +04:00
void * old_handler ;
2006-11-21 07:34:04 +03:00
2006-10-19 11:20:25 +04:00
old_handler = exception_handling_table [ vec ] ;
exception_handling_table [ vec ] = handler ;
return old_handler ;
}
2005-04-17 02:20:36 +04:00
2006-10-19 11:20:25 +04:00
void __init trap_init ( void )
{
set_exception_table_vec ( TRAP_RESERVED_INST , do_reserved_inst ) ;
set_exception_table_vec ( TRAP_ILLEGAL_SLOT_INST , do_illegal_slot_inst ) ;
2005-04-17 02:20:36 +04:00
2006-09-27 12:15:32 +04:00
# if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SH_FPU) || \
defined ( CONFIG_SH_FPU_EMU )
/*
* For SH - 4 lacking an FPU , treat floating point instructions as
* reserved . They ' ll be handled in the math - emu case , or faulted on
* otherwise .
*/
2006-10-19 11:20:25 +04:00
set_exception_table_evt ( 0x800 , do_reserved_inst ) ;
set_exception_table_evt ( 0x820 , do_illegal_slot_inst ) ;
# elif defined(CONFIG_SH_FPU)
2007-11-26 14:38:36 +03:00
set_exception_table_evt ( 0x800 , fpu_state_restore_trap_handler ) ;
set_exception_table_evt ( 0x820 , fpu_state_restore_trap_handler ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-11-05 09:58:47 +03:00
# ifdef CONFIG_CPU_SH2
2007-11-20 12:08:06 +03:00
set_exception_table_vec ( TRAP_ADDRESS_ERROR , address_error_trap_handler ) ;
2006-11-05 09:58:47 +03:00
# endif
# ifdef CONFIG_CPU_SH2A
set_exception_table_vec ( TRAP_DIVZERO_ERROR , do_divide_error ) ;
set_exception_table_vec ( TRAP_DIVOVF_ERROR , do_divide_error ) ;
2008-07-09 20:20:03 +04:00
# ifdef CONFIG_SH_FPU
set_exception_table_vec ( TRAP_FPU_ERROR , fpu_error_trap_handler ) ;
# endif
2006-11-05 09:58:47 +03:00
# endif
2006-11-21 07:34:04 +03:00
2009-05-08 18:51:51 +04:00
# ifdef TRAP_UBC
2010-01-05 06:44:02 +03:00
set_exception_table_vec ( TRAP_UBC , breakpoint_trap_handler ) ;
2009-05-08 18:51:51 +04:00
# endif
2005-04-17 02:20:36 +04:00
}