forked from mirrors/linux
		
	sh: Wire up division and address error exceptions on SH-2A.
SH-2A has special division hardware as opposed to a full-fledged FPU, wire up the exception handlers for this. Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
		
							parent
							
								
									9d4436a6fb
								
							
						
					
					
						commit
						0983b31849
					
				
					 1 changed files with 79 additions and 9 deletions
				
			
		| 
						 | 
					@ -33,8 +33,13 @@
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_CPU_SH2
 | 
					#ifdef CONFIG_CPU_SH2
 | 
				
			||||||
#define TRAP_RESERVED_INST	4
 | 
					# define TRAP_RESERVED_INST	4
 | 
				
			||||||
#define TRAP_ILLEGAL_SLOT_INST	6
 | 
					# define TRAP_ILLEGAL_SLOT_INST	6
 | 
				
			||||||
 | 
					# define TRAP_ADDRESS_ERROR	9
 | 
				
			||||||
 | 
					# ifdef CONFIG_CPU_SH2A
 | 
				
			||||||
 | 
					#  define TRAP_DIVZERO_ERROR	17
 | 
				
			||||||
 | 
					#  define TRAP_DIVOVF_ERROR	18
 | 
				
			||||||
 | 
					# endif
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#define TRAP_RESERVED_INST	12
 | 
					#define TRAP_RESERVED_INST	12
 | 
				
			||||||
#define TRAP_ILLEGAL_SLOT_INST	13
 | 
					#define TRAP_ILLEGAL_SLOT_INST	13
 | 
				
			||||||
| 
						 | 
					@ -479,6 +484,14 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_CPU_HAS_SR_RB
 | 
				
			||||||
 | 
					#define lookup_exception_vector(x)	\
 | 
				
			||||||
 | 
						__asm__ __volatile__ ("stc r2_bank, %0\n\t" : "=r" ((x)))
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define lookup_exception_vector(x)	\
 | 
				
			||||||
 | 
						__asm__ __volatile__ ("mov r4, %0\n\t" : "=r" ((x)))
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Handle various address error exceptions
 | 
					 * Handle various address error exceptions
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -486,24 +499,37 @@ asmlinkage void do_address_error(struct pt_regs *regs,
 | 
				
			||||||
				 unsigned long writeaccess,
 | 
									 unsigned long writeaccess,
 | 
				
			||||||
				 unsigned long address)
 | 
									 unsigned long address)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned long error_code;
 | 
						unsigned long error_code = 0;
 | 
				
			||||||
	mm_segment_t oldfs;
 | 
						mm_segment_t oldfs;
 | 
				
			||||||
	u16 instruction;
 | 
						u16 instruction;
 | 
				
			||||||
	int tmp;
 | 
						int tmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	asm volatile("stc       r2_bank,%0": "=r" (error_code));
 | 
						/* Intentional ifdef */
 | 
				
			||||||
 | 
					#ifdef CONFIG_CPU_HAS_SR_RB
 | 
				
			||||||
 | 
						lookup_exception_vector(error_code);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	oldfs = get_fs();
 | 
						oldfs = get_fs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (user_mode(regs)) {
 | 
						if (user_mode(regs)) {
 | 
				
			||||||
		local_irq_enable();
 | 
							local_irq_enable();
 | 
				
			||||||
		current->thread.error_code = error_code;
 | 
							current->thread.error_code = error_code;
 | 
				
			||||||
 | 
					#ifdef CONFIG_CPU_SH2
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * On the SH-2, we only have a single vector for address
 | 
				
			||||||
 | 
							 * errors, there's no differentiating between a load error
 | 
				
			||||||
 | 
							 * and a store error.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							current->thread.trap_no = 9;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
		current->thread.trap_no = (writeaccess) ? 8 : 7;
 | 
							current->thread.trap_no = (writeaccess) ? 8 : 7;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* bad PC is not something we can fix */
 | 
							/* bad PC is not something we can fix */
 | 
				
			||||||
		if (regs->pc & 1)
 | 
							if (regs->pc & 1)
 | 
				
			||||||
			goto uspace_segv;
 | 
								goto uspace_segv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef CONFIG_CPU_SH2A
 | 
				
			||||||
		set_fs(USER_DS);
 | 
							set_fs(USER_DS);
 | 
				
			||||||
		if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
 | 
							if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
 | 
				
			||||||
			/* Argh. Fault on the instruction itself.
 | 
								/* Argh. Fault on the instruction itself.
 | 
				
			||||||
| 
						 | 
					@ -518,6 +544,7 @@ asmlinkage void do_address_error(struct pt_regs *regs,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (tmp==0)
 | 
							if (tmp==0)
 | 
				
			||||||
			return; /* sorted */
 | 
								return; /* sorted */
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uspace_segv:
 | 
						uspace_segv:
 | 
				
			||||||
		printk(KERN_NOTICE "Killing process \"%s\" due to unaligned access\n", current->comm);
 | 
							printk(KERN_NOTICE "Killing process \"%s\" due to unaligned access\n", current->comm);
 | 
				
			||||||
| 
						 | 
					@ -526,6 +553,7 @@ asmlinkage void do_address_error(struct pt_regs *regs,
 | 
				
			||||||
		if (regs->pc & 1)
 | 
							if (regs->pc & 1)
 | 
				
			||||||
			die("unaligned program counter", regs, error_code);
 | 
								die("unaligned program counter", regs, error_code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef CONFIG_CPU_SH2A
 | 
				
			||||||
		set_fs(KERNEL_DS);
 | 
							set_fs(KERNEL_DS);
 | 
				
			||||||
		if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
 | 
							if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
 | 
				
			||||||
			/* Argh. Fault on the instruction itself.
 | 
								/* Argh. Fault on the instruction itself.
 | 
				
			||||||
| 
						 | 
					@ -537,6 +565,10 @@ asmlinkage void do_address_error(struct pt_regs *regs,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		handle_unaligned_access(instruction, regs);
 | 
							handle_unaligned_access(instruction, regs);
 | 
				
			||||||
		set_fs(oldfs);
 | 
							set_fs(oldfs);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							printk(KERN_NOTICE "Killing process \"%s\" due to unaligned access\n", current->comm);
 | 
				
			||||||
 | 
							force_sig(SIGSEGV, current);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -569,6 +601,29 @@ int is_dsp_inst(struct pt_regs *regs)
 | 
				
			||||||
#define is_dsp_inst(regs)	(0)
 | 
					#define is_dsp_inst(regs)	(0)
 | 
				
			||||||
#endif /* CONFIG_SH_DSP */
 | 
					#endif /* CONFIG_SH_DSP */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_CPU_SH2A
 | 
				
			||||||
 | 
					asmlinkage void do_divide_error(unsigned long r4, unsigned long r5,
 | 
				
			||||||
 | 
									unsigned long r6, unsigned long r7,
 | 
				
			||||||
 | 
									struct pt_regs regs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						siginfo_t info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						current->thread.trap_no = r4;
 | 
				
			||||||
 | 
						current->thread.error_code = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (r4) {
 | 
				
			||||||
 | 
						case TRAP_DIVZERO_ERROR:
 | 
				
			||||||
 | 
							info.si_code = FPE_INTDIV;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case TRAP_DIVOVF_ERROR:
 | 
				
			||||||
 | 
							info.si_code = FPE_INTOVF;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						force_sig_info(SIGFPE, &info, current);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* arch/sh/kernel/cpu/sh4/fpu.c */
 | 
					/* arch/sh/kernel/cpu/sh4/fpu.c */
 | 
				
			||||||
extern int do_fpu_inst(unsigned short, struct pt_regs *);
 | 
					extern int do_fpu_inst(unsigned short, struct pt_regs *);
 | 
				
			||||||
extern asmlinkage void do_fpu_state_restore(unsigned long r4, unsigned long r5,
 | 
					extern asmlinkage void do_fpu_state_restore(unsigned long r4, unsigned long r5,
 | 
				
			||||||
| 
						 | 
					@ -582,7 +637,7 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5,
 | 
				
			||||||
	struct task_struct *tsk = current;
 | 
						struct task_struct *tsk = current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_SH_FPU_EMU
 | 
					#ifdef CONFIG_SH_FPU_EMU
 | 
				
			||||||
	unsigned short inst;
 | 
						unsigned short inst = 0;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	get_user(inst, (unsigned short*)regs.pc);
 | 
						get_user(inst, (unsigned short*)regs.pc);
 | 
				
			||||||
| 
						 | 
					@ -604,7 +659,8 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	asm volatile("stc	r2_bank, %0": "=r" (error_code));
 | 
						lookup_exception_vector(error_code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local_irq_enable();
 | 
						local_irq_enable();
 | 
				
			||||||
	tsk->thread.error_code = error_code;
 | 
						tsk->thread.error_code = error_code;
 | 
				
			||||||
	tsk->thread.trap_no = TRAP_RESERVED_INST;
 | 
						tsk->thread.trap_no = TRAP_RESERVED_INST;
 | 
				
			||||||
| 
						 | 
					@ -663,7 +719,7 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5,
 | 
				
			||||||
	unsigned long error_code;
 | 
						unsigned long error_code;
 | 
				
			||||||
	struct task_struct *tsk = current;
 | 
						struct task_struct *tsk = current;
 | 
				
			||||||
#ifdef CONFIG_SH_FPU_EMU
 | 
					#ifdef CONFIG_SH_FPU_EMU
 | 
				
			||||||
	unsigned short inst;
 | 
						unsigned short inst = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	get_user(inst, (unsigned short *)regs.pc + 1);
 | 
						get_user(inst, (unsigned short *)regs.pc + 1);
 | 
				
			||||||
	if (!do_fpu_inst(inst, ®s)) {
 | 
						if (!do_fpu_inst(inst, ®s)) {
 | 
				
			||||||
| 
						 | 
					@ -675,7 +731,8 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5,
 | 
				
			||||||
	/* not a FPU inst. */
 | 
						/* not a FPU inst. */
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	asm volatile("stc	r2_bank, %0": "=r" (error_code));
 | 
						lookup_exception_vector(error_code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local_irq_enable();
 | 
						local_irq_enable();
 | 
				
			||||||
	tsk->thread.error_code = error_code;
 | 
						tsk->thread.error_code = error_code;
 | 
				
			||||||
	tsk->thread.trap_no = TRAP_RESERVED_INST;
 | 
						tsk->thread.trap_no = TRAP_RESERVED_INST;
 | 
				
			||||||
| 
						 | 
					@ -689,7 +746,8 @@ asmlinkage void do_exception_error(unsigned long r4, unsigned long r5,
 | 
				
			||||||
				   struct pt_regs regs)
 | 
									   struct pt_regs regs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	long ex;
 | 
						long ex;
 | 
				
			||||||
	asm volatile("stc	r2_bank, %0" : "=r" (ex));
 | 
					
 | 
				
			||||||
 | 
						lookup_exception_vector(ex);
 | 
				
			||||||
	die_if_kernel("exception", ®s, ex);
 | 
						die_if_kernel("exception", ®s, ex);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -741,6 +799,10 @@ void *set_exception_table_vec(unsigned int vec, void *handler)
 | 
				
			||||||
	return old_handler;
 | 
						return old_handler;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern asmlinkage void address_error_handler(unsigned long r4, unsigned long r5,
 | 
				
			||||||
 | 
										     unsigned long r6, unsigned long r7,
 | 
				
			||||||
 | 
										     struct pt_regs regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void __init trap_init(void)
 | 
					void __init trap_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	set_exception_table_vec(TRAP_RESERVED_INST, do_reserved_inst);
 | 
						set_exception_table_vec(TRAP_RESERVED_INST, do_reserved_inst);
 | 
				
			||||||
| 
						 | 
					@ -759,6 +821,14 @@ void __init trap_init(void)
 | 
				
			||||||
	set_exception_table_evt(0x800, do_fpu_state_restore);
 | 
						set_exception_table_evt(0x800, do_fpu_state_restore);
 | 
				
			||||||
	set_exception_table_evt(0x820, do_fpu_state_restore);
 | 
						set_exception_table_evt(0x820, do_fpu_state_restore);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_CPU_SH2
 | 
				
			||||||
 | 
						set_exception_table_vec(TRAP_ADDRESS_ERROR, address_error_handler);
 | 
				
			||||||
 | 
					#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);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
	/* Setup VBR for boot cpu */
 | 
						/* Setup VBR for boot cpu */
 | 
				
			||||||
	per_cpu_trap_init();
 | 
						per_cpu_trap_init();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue