aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/traps.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/kernel/traps.c')
-rw-r--r--arch/sh/kernel/traps.c164
1 files changed, 125 insertions, 39 deletions
diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c
index 21bef1b5f991..95c810b3c97e 100644
--- a/arch/sh/kernel/traps.c
+++ b/arch/sh/kernel/traps.c
@@ -36,40 +36,15 @@
36 36
37#ifdef CONFIG_SH_KGDB 37#ifdef CONFIG_SH_KGDB
38#include <asm/kgdb.h> 38#include <asm/kgdb.h>
39#define CHK_REMOTE_DEBUG(regs) \ 39#define CHK_REMOTE_DEBUG(regs) \
40{ \ 40{ \
41 if ((kgdb_debug_hook != (kgdb_debug_hook_t *) NULL) && (!user_mode(regs))) \ 41 if (kgdb_debug_hook && !user_mode(regs))\
42 { \ 42 (*kgdb_debug_hook)(regs); \
43 (*kgdb_debug_hook)(regs); \
44 } \
45} 43}
46#else 44#else
47#define CHK_REMOTE_DEBUG(regs) 45#define CHK_REMOTE_DEBUG(regs)
48#endif 46#endif
49 47
50#define DO_ERROR(trapnr, signr, str, name, tsk) \
51asmlinkage void do_##name(unsigned long r4, unsigned long r5, \
52 unsigned long r6, unsigned long r7, \
53 struct pt_regs regs) \
54{ \
55 unsigned long error_code; \
56 \
57 /* Check if it's a DSP instruction */ \
58 if (is_dsp_inst(&regs)) { \
59 /* Enable DSP mode, and restart instruction. */ \
60 regs.sr |= SR_DSP; \
61 return; \
62 } \
63 \
64 asm volatile("stc r2_bank, %0": "=r" (error_code)); \
65 local_irq_enable(); \
66 tsk->thread.error_code = error_code; \
67 tsk->thread.trap_no = trapnr; \
68 CHK_REMOTE_DEBUG(&regs); \
69 force_sig(signr, tsk); \
70 die_if_no_fixup(str,&regs,error_code); \
71}
72
73#ifdef CONFIG_CPU_SH2 48#ifdef CONFIG_CPU_SH2
74#define TRAP_RESERVED_INST 4 49#define TRAP_RESERVED_INST 4
75#define TRAP_ILLEGAL_SLOT_INST 6 50#define TRAP_ILLEGAL_SLOT_INST 6
@@ -575,8 +550,117 @@ int is_dsp_inst(struct pt_regs *regs)
575#define is_dsp_inst(regs) (0) 550#define is_dsp_inst(regs) (0)
576#endif /* CONFIG_SH_DSP */ 551#endif /* CONFIG_SH_DSP */
577 552
578DO_ERROR(TRAP_RESERVED_INST, SIGILL, "reserved instruction", reserved_inst, current) 553extern int do_fpu_inst(unsigned short, struct pt_regs*);
579DO_ERROR(TRAP_ILLEGAL_SLOT_INST, SIGILL, "illegal slot instruction", illegal_slot_inst, current) 554
555asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5,
556 unsigned long r6, unsigned long r7,
557 struct pt_regs regs)
558{
559 unsigned long error_code;
560 struct task_struct *tsk = current;
561
562#ifdef CONFIG_SH_FPU_EMU
563 unsigned short inst;
564 int err;
565
566 get_user(inst, (unsigned short*)regs.pc);
567
568 err = do_fpu_inst(inst, &regs);
569 if (!err) {
570 regs.pc += 2;
571 return;
572 }
573 /* not a FPU inst. */
574#endif
575
576#ifdef CONFIG_SH_DSP
577 /* Check if it's a DSP instruction */
578 if (is_dsp_inst(&regs)) {
579 /* Enable DSP mode, and restart instruction. */
580 regs.sr |= SR_DSP;
581 return;
582 }
583#endif
584
585 asm volatile("stc r2_bank, %0": "=r" (error_code));
586 local_irq_enable();
587 tsk->thread.error_code = error_code;
588 tsk->thread.trap_no = TRAP_RESERVED_INST;
589 CHK_REMOTE_DEBUG(&regs);
590 force_sig(SIGILL, tsk);
591 die_if_no_fixup("reserved instruction", &regs, error_code);
592}
593
594#ifdef CONFIG_SH_FPU_EMU
595static int emulate_branch(unsigned short inst, struct pt_regs* regs)
596{
597 /*
598 * bfs: 8fxx: PC+=d*2+4;
599 * bts: 8dxx: PC+=d*2+4;
600 * bra: axxx: PC+=D*2+4;
601 * bsr: bxxx: PC+=D*2+4 after PR=PC+4;
602 * braf:0x23: PC+=Rn*2+4;
603 * bsrf:0x03: PC+=Rn*2+4 after PR=PC+4;
604 * jmp: 4x2b: PC=Rn;
605 * jsr: 4x0b: PC=Rn after PR=PC+4;
606 * rts: 000b: PC=PR;
607 */
608 if ((inst & 0xfd00) == 0x8d00) {
609 regs->pc += SH_PC_8BIT_OFFSET(inst);
610 return 0;
611 }
612
613 if ((inst & 0xe000) == 0xa000) {
614 regs->pc += SH_PC_12BIT_OFFSET(inst);
615 return 0;
616 }
617
618 if ((inst & 0xf0df) == 0x0003) {
619 regs->pc += regs->regs[(inst & 0x0f00) >> 8] + 4;
620 return 0;
621 }
622
623 if ((inst & 0xf0df) == 0x400b) {
624 regs->pc = regs->regs[(inst & 0x0f00) >> 8];
625 return 0;
626 }
627
628 if ((inst & 0xffff) == 0x000b) {
629 regs->pc = regs->pr;
630 return 0;
631 }
632
633 return 1;
634}
635#endif
636
637asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5,
638 unsigned long r6, unsigned long r7,
639 struct pt_regs regs)
640{
641 unsigned long error_code;
642 struct task_struct *tsk = current;
643#ifdef CONFIG_SH_FPU_EMU
644 unsigned short inst;
645
646 get_user(inst, (unsigned short *)regs.pc + 1);
647 if (!do_fpu_inst(inst, &regs)) {
648 get_user(inst, (unsigned short *)regs.pc);
649 if (!emulate_branch(inst, &regs))
650 return;
651 /* fault in branch.*/
652 }
653 /* not a FPU inst. */
654#endif
655
656 asm volatile("stc r2_bank, %0": "=r" (error_code));
657 local_irq_enable();
658 tsk->thread.error_code = error_code;
659 tsk->thread.trap_no = TRAP_RESERVED_INST;
660 CHK_REMOTE_DEBUG(&regs);
661 force_sig(SIGILL, tsk);
662 die_if_no_fixup("illegal slot instruction", &regs, error_code);
663}
580 664
581asmlinkage void do_exception_error(unsigned long r4, unsigned long r5, 665asmlinkage void do_exception_error(unsigned long r4, unsigned long r5,
582 unsigned long r6, unsigned long r7, 666 unsigned long r6, unsigned long r7,
@@ -634,14 +718,16 @@ void __init trap_init(void)
634 exception_handling_table[TRAP_ILLEGAL_SLOT_INST] 718 exception_handling_table[TRAP_ILLEGAL_SLOT_INST]
635 = (void *)do_illegal_slot_inst; 719 = (void *)do_illegal_slot_inst;
636 720
637#ifdef CONFIG_CPU_SH4 721#if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SH_FPU) || \
638 if (!(cpu_data->flags & CPU_HAS_FPU)) { 722 defined(CONFIG_SH_FPU_EMU)
639 /* For SH-4 lacking an FPU, treat floating point instructions 723 /*
640 as reserved. */ 724 * For SH-4 lacking an FPU, treat floating point instructions as
641 /* entry 64 corresponds to EXPEVT=0x800 */ 725 * reserved. They'll be handled in the math-emu case, or faulted on
642 exception_handling_table[64] = (void *)do_reserved_inst; 726 * otherwise.
643 exception_handling_table[65] = (void *)do_illegal_slot_inst; 727 */
644 } 728 /* entry 64 corresponds to EXPEVT=0x800 */
729 exception_handling_table[64] = (void *)do_reserved_inst;
730 exception_handling_table[65] = (void *)do_illegal_slot_inst;
645#endif 731#endif
646 732
647 /* Setup VBR for boot cpu */ 733 /* Setup VBR for boot cpu */