Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0983b318 authored by Yoshinori Sato's avatar Yoshinori Sato Committed by Paul Mundt
Browse files

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: default avatarYoshinori Sato <ysato@users.sourceforge.jp>
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent 9d4436a6
Loading
Loading
Loading
Loading
+79 −9
Original line number Diff line number Diff line
@@ -35,6 +35,11 @@
#ifdef CONFIG_CPU_SH2
# define TRAP_RESERVED_INST	4
# 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
#define TRAP_RESERVED_INST	12
#define TRAP_ILLEGAL_SLOT_INST	13
@@ -479,6 +484,14 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
	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
 */
@@ -486,24 +499,37 @@ asmlinkage void do_address_error(struct pt_regs *regs,
				 unsigned long writeaccess,
				 unsigned long address)
{
	unsigned long error_code;
	unsigned long error_code = 0;
	mm_segment_t oldfs;
	u16 instruction;
	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();

	if (user_mode(regs)) {
		local_irq_enable();
		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;
#endif

		/* bad PC is not something we can fix */
		if (regs->pc & 1)
			goto uspace_segv;

#ifndef CONFIG_CPU_SH2A
		set_fs(USER_DS);
		if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
			/* Argh. Fault on the instruction itself.
@@ -518,6 +544,7 @@ asmlinkage void do_address_error(struct pt_regs *regs,

		if (tmp==0)
			return; /* sorted */
#endif

	uspace_segv:
		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)
			die("unaligned program counter", regs, error_code);

#ifndef CONFIG_CPU_SH2A
		set_fs(KERNEL_DS);
		if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
			/* Argh. Fault on the instruction itself.
@@ -537,6 +565,10 @@ asmlinkage void do_address_error(struct pt_regs *regs,

		handle_unaligned_access(instruction, regs);
		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)
#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 */
extern int do_fpu_inst(unsigned short, struct pt_regs *);
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;

#ifdef CONFIG_SH_FPU_EMU
	unsigned short inst;
	unsigned short inst = 0;
	int err;

	get_user(inst, (unsigned short*)regs.pc);
@@ -604,7 +659,8 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5,
	}
#endif

	asm volatile("stc	r2_bank, %0": "=r" (error_code));
	lookup_exception_vector(error_code);

	local_irq_enable();
	tsk->thread.error_code = error_code;
	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;
	struct task_struct *tsk = current;
#ifdef CONFIG_SH_FPU_EMU
	unsigned short inst;
	unsigned short inst = 0;

	get_user(inst, (unsigned short *)regs.pc + 1);
	if (!do_fpu_inst(inst, &regs)) {
@@ -675,7 +731,8 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5,
	/* not a FPU inst. */
#endif

	asm volatile("stc	r2_bank, %0": "=r" (error_code));
	lookup_exception_vector(error_code);

	local_irq_enable();
	tsk->thread.error_code = error_code;
	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)
{
	long ex;
	asm volatile("stc	r2_bank, %0" : "=r" (ex));

	lookup_exception_vector(ex);
	die_if_kernel("exception", &regs, ex);
}

@@ -741,6 +799,10 @@ void *set_exception_table_vec(unsigned int vec, void *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)
{
	set_exception_table_vec(TRAP_RESERVED_INST, do_reserved_inst);
@@ -760,6 +822,14 @@ void __init trap_init(void)
	set_exception_table_evt(0x820, do_fpu_state_restore);
#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 */
	per_cpu_trap_init();
}