diff options
Diffstat (limited to 'arch/sh/kernel/traps.c')
-rw-r--r-- | arch/sh/kernel/traps.c | 164 |
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) \ | ||
51 | asmlinkage 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(®s)) { \ | ||
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(®s); \ | ||
69 | force_sig(signr, tsk); \ | ||
70 | die_if_no_fixup(str,®s,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 | ||
578 | DO_ERROR(TRAP_RESERVED_INST, SIGILL, "reserved instruction", reserved_inst, current) | 553 | extern int do_fpu_inst(unsigned short, struct pt_regs*); |
579 | DO_ERROR(TRAP_ILLEGAL_SLOT_INST, SIGILL, "illegal slot instruction", illegal_slot_inst, current) | 554 | |
555 | asmlinkage 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, ®s); | ||
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(®s)) { | ||
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(®s); | ||
590 | force_sig(SIGILL, tsk); | ||
591 | die_if_no_fixup("reserved instruction", ®s, error_code); | ||
592 | } | ||
593 | |||
594 | #ifdef CONFIG_SH_FPU_EMU | ||
595 | static 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 | |||
637 | asmlinkage 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, ®s)) { | ||
648 | get_user(inst, (unsigned short *)regs.pc); | ||
649 | if (!emulate_branch(inst, ®s)) | ||
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(®s); | ||
661 | force_sig(SIGILL, tsk); | ||
662 | die_if_no_fixup("illegal slot instruction", ®s, error_code); | ||
663 | } | ||
580 | 664 | ||
581 | asmlinkage void do_exception_error(unsigned long r4, unsigned long r5, | 665 | asmlinkage 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 */ |