diff options
author | Yoshinori Sato <ysato@users.sourceforge.jp> | 2006-11-05 01:58:47 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2006-12-05 20:45:36 -0500 |
commit | 0983b31849bbe1fe82520947acc1bec97c457d4b (patch) | |
tree | dd8990e4845bdb7c3eaa8daf994d2112dd1ab26a /arch/sh/kernel/traps.c | |
parent | 9d4436a6fbc8c5eccdfcb8f5884e0a7b4a57f6d2 (diff) |
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>
Diffstat (limited to 'arch/sh/kernel/traps.c')
-rw-r--r-- | arch/sh/kernel/traps.c | 88 |
1 files changed, 79 insertions, 9 deletions
diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 53dfa55f3156..1edec2709efe 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c | |||
@@ -33,8 +33,13 @@ | |||
33 | #endif | 33 | #endif |
34 | 34 | ||
35 | #ifdef CONFIG_CPU_SH2 | 35 | #ifdef CONFIG_CPU_SH2 |
36 | #define TRAP_RESERVED_INST 4 | 36 | # define TRAP_RESERVED_INST 4 |
37 | #define TRAP_ILLEGAL_SLOT_INST 6 | 37 | # define TRAP_ILLEGAL_SLOT_INST 6 |
38 | # define TRAP_ADDRESS_ERROR 9 | ||
39 | # ifdef CONFIG_CPU_SH2A | ||
40 | # define TRAP_DIVZERO_ERROR 17 | ||
41 | # define TRAP_DIVOVF_ERROR 18 | ||
42 | # endif | ||
38 | #else | 43 | #else |
39 | #define TRAP_RESERVED_INST 12 | 44 | #define TRAP_RESERVED_INST 12 |
40 | #define TRAP_ILLEGAL_SLOT_INST 13 | 45 | #define TRAP_ILLEGAL_SLOT_INST 13 |
@@ -479,6 +484,14 @@ static int handle_unaligned_access(u16 instruction, struct pt_regs *regs) | |||
479 | return ret; | 484 | return ret; |
480 | } | 485 | } |
481 | 486 | ||
487 | #ifdef CONFIG_CPU_HAS_SR_RB | ||
488 | #define lookup_exception_vector(x) \ | ||
489 | __asm__ __volatile__ ("stc r2_bank, %0\n\t" : "=r" ((x))) | ||
490 | #else | ||
491 | #define lookup_exception_vector(x) \ | ||
492 | __asm__ __volatile__ ("mov r4, %0\n\t" : "=r" ((x))) | ||
493 | #endif | ||
494 | |||
482 | /* | 495 | /* |
483 | * Handle various address error exceptions | 496 | * Handle various address error exceptions |
484 | */ | 497 | */ |
@@ -486,24 +499,37 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
486 | unsigned long writeaccess, | 499 | unsigned long writeaccess, |
487 | unsigned long address) | 500 | unsigned long address) |
488 | { | 501 | { |
489 | unsigned long error_code; | 502 | unsigned long error_code = 0; |
490 | mm_segment_t oldfs; | 503 | mm_segment_t oldfs; |
491 | u16 instruction; | 504 | u16 instruction; |
492 | int tmp; | 505 | int tmp; |
493 | 506 | ||
494 | asm volatile("stc r2_bank,%0": "=r" (error_code)); | 507 | /* Intentional ifdef */ |
508 | #ifdef CONFIG_CPU_HAS_SR_RB | ||
509 | lookup_exception_vector(error_code); | ||
510 | #endif | ||
495 | 511 | ||
496 | oldfs = get_fs(); | 512 | oldfs = get_fs(); |
497 | 513 | ||
498 | if (user_mode(regs)) { | 514 | if (user_mode(regs)) { |
499 | local_irq_enable(); | 515 | local_irq_enable(); |
500 | current->thread.error_code = error_code; | 516 | current->thread.error_code = error_code; |
517 | #ifdef CONFIG_CPU_SH2 | ||
518 | /* | ||
519 | * On the SH-2, we only have a single vector for address | ||
520 | * errors, there's no differentiating between a load error | ||
521 | * and a store error. | ||
522 | */ | ||
523 | current->thread.trap_no = 9; | ||
524 | #else | ||
501 | current->thread.trap_no = (writeaccess) ? 8 : 7; | 525 | current->thread.trap_no = (writeaccess) ? 8 : 7; |
526 | #endif | ||
502 | 527 | ||
503 | /* bad PC is not something we can fix */ | 528 | /* bad PC is not something we can fix */ |
504 | if (regs->pc & 1) | 529 | if (regs->pc & 1) |
505 | goto uspace_segv; | 530 | goto uspace_segv; |
506 | 531 | ||
532 | #ifndef CONFIG_CPU_SH2A | ||
507 | set_fs(USER_DS); | 533 | set_fs(USER_DS); |
508 | if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) { | 534 | if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) { |
509 | /* Argh. Fault on the instruction itself. | 535 | /* Argh. Fault on the instruction itself. |
@@ -518,6 +544,7 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
518 | 544 | ||
519 | if (tmp==0) | 545 | if (tmp==0) |
520 | return; /* sorted */ | 546 | return; /* sorted */ |
547 | #endif | ||
521 | 548 | ||
522 | uspace_segv: | 549 | uspace_segv: |
523 | printk(KERN_NOTICE "Killing process \"%s\" due to unaligned access\n", current->comm); | 550 | 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, | |||
526 | if (regs->pc & 1) | 553 | if (regs->pc & 1) |
527 | die("unaligned program counter", regs, error_code); | 554 | die("unaligned program counter", regs, error_code); |
528 | 555 | ||
556 | #ifndef CONFIG_CPU_SH2A | ||
529 | set_fs(KERNEL_DS); | 557 | set_fs(KERNEL_DS); |
530 | if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) { | 558 | if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) { |
531 | /* Argh. Fault on the instruction itself. | 559 | /* Argh. Fault on the instruction itself. |
@@ -537,6 +565,10 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
537 | 565 | ||
538 | handle_unaligned_access(instruction, regs); | 566 | handle_unaligned_access(instruction, regs); |
539 | set_fs(oldfs); | 567 | set_fs(oldfs); |
568 | #else | ||
569 | printk(KERN_NOTICE "Killing process \"%s\" due to unaligned access\n", current->comm); | ||
570 | force_sig(SIGSEGV, current); | ||
571 | #endif | ||
540 | } | 572 | } |
541 | } | 573 | } |
542 | 574 | ||
@@ -569,6 +601,29 @@ int is_dsp_inst(struct pt_regs *regs) | |||
569 | #define is_dsp_inst(regs) (0) | 601 | #define is_dsp_inst(regs) (0) |
570 | #endif /* CONFIG_SH_DSP */ | 602 | #endif /* CONFIG_SH_DSP */ |
571 | 603 | ||
604 | #ifdef CONFIG_CPU_SH2A | ||
605 | asmlinkage void do_divide_error(unsigned long r4, unsigned long r5, | ||
606 | unsigned long r6, unsigned long r7, | ||
607 | struct pt_regs regs) | ||
608 | { | ||
609 | siginfo_t info; | ||
610 | |||
611 | current->thread.trap_no = r4; | ||
612 | current->thread.error_code = 0; | ||
613 | |||
614 | switch (r4) { | ||
615 | case TRAP_DIVZERO_ERROR: | ||
616 | info.si_code = FPE_INTDIV; | ||
617 | break; | ||
618 | case TRAP_DIVOVF_ERROR: | ||
619 | info.si_code = FPE_INTOVF; | ||
620 | break; | ||
621 | } | ||
622 | |||
623 | force_sig_info(SIGFPE, &info, current); | ||
624 | } | ||
625 | #endif | ||
626 | |||
572 | /* arch/sh/kernel/cpu/sh4/fpu.c */ | 627 | /* arch/sh/kernel/cpu/sh4/fpu.c */ |
573 | extern int do_fpu_inst(unsigned short, struct pt_regs *); | 628 | extern int do_fpu_inst(unsigned short, struct pt_regs *); |
574 | extern asmlinkage void do_fpu_state_restore(unsigned long r4, unsigned long r5, | 629 | 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, | |||
582 | struct task_struct *tsk = current; | 637 | struct task_struct *tsk = current; |
583 | 638 | ||
584 | #ifdef CONFIG_SH_FPU_EMU | 639 | #ifdef CONFIG_SH_FPU_EMU |
585 | unsigned short inst; | 640 | unsigned short inst = 0; |
586 | int err; | 641 | int err; |
587 | 642 | ||
588 | get_user(inst, (unsigned short*)regs.pc); | 643 | get_user(inst, (unsigned short*)regs.pc); |
@@ -604,7 +659,8 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, | |||
604 | } | 659 | } |
605 | #endif | 660 | #endif |
606 | 661 | ||
607 | asm volatile("stc r2_bank, %0": "=r" (error_code)); | 662 | lookup_exception_vector(error_code); |
663 | |||
608 | local_irq_enable(); | 664 | local_irq_enable(); |
609 | tsk->thread.error_code = error_code; | 665 | tsk->thread.error_code = error_code; |
610 | tsk->thread.trap_no = TRAP_RESERVED_INST; | 666 | tsk->thread.trap_no = TRAP_RESERVED_INST; |
@@ -663,7 +719,7 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, | |||
663 | unsigned long error_code; | 719 | unsigned long error_code; |
664 | struct task_struct *tsk = current; | 720 | struct task_struct *tsk = current; |
665 | #ifdef CONFIG_SH_FPU_EMU | 721 | #ifdef CONFIG_SH_FPU_EMU |
666 | unsigned short inst; | 722 | unsigned short inst = 0; |
667 | 723 | ||
668 | get_user(inst, (unsigned short *)regs.pc + 1); | 724 | get_user(inst, (unsigned short *)regs.pc + 1); |
669 | if (!do_fpu_inst(inst, ®s)) { | 725 | if (!do_fpu_inst(inst, ®s)) { |
@@ -675,7 +731,8 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, | |||
675 | /* not a FPU inst. */ | 731 | /* not a FPU inst. */ |
676 | #endif | 732 | #endif |
677 | 733 | ||
678 | asm volatile("stc r2_bank, %0": "=r" (error_code)); | 734 | lookup_exception_vector(error_code); |
735 | |||
679 | local_irq_enable(); | 736 | local_irq_enable(); |
680 | tsk->thread.error_code = error_code; | 737 | tsk->thread.error_code = error_code; |
681 | tsk->thread.trap_no = TRAP_RESERVED_INST; | 738 | tsk->thread.trap_no = TRAP_RESERVED_INST; |
@@ -689,7 +746,8 @@ asmlinkage void do_exception_error(unsigned long r4, unsigned long r5, | |||
689 | struct pt_regs regs) | 746 | struct pt_regs regs) |
690 | { | 747 | { |
691 | long ex; | 748 | long ex; |
692 | asm volatile("stc r2_bank, %0" : "=r" (ex)); | 749 | |
750 | lookup_exception_vector(ex); | ||
693 | die_if_kernel("exception", ®s, ex); | 751 | die_if_kernel("exception", ®s, ex); |
694 | } | 752 | } |
695 | 753 | ||
@@ -741,6 +799,10 @@ void *set_exception_table_vec(unsigned int vec, void *handler) | |||
741 | return old_handler; | 799 | return old_handler; |
742 | } | 800 | } |
743 | 801 | ||
802 | extern asmlinkage void address_error_handler(unsigned long r4, unsigned long r5, | ||
803 | unsigned long r6, unsigned long r7, | ||
804 | struct pt_regs regs); | ||
805 | |||
744 | void __init trap_init(void) | 806 | void __init trap_init(void) |
745 | { | 807 | { |
746 | set_exception_table_vec(TRAP_RESERVED_INST, do_reserved_inst); | 808 | set_exception_table_vec(TRAP_RESERVED_INST, do_reserved_inst); |
@@ -759,6 +821,14 @@ void __init trap_init(void) | |||
759 | set_exception_table_evt(0x800, do_fpu_state_restore); | 821 | set_exception_table_evt(0x800, do_fpu_state_restore); |
760 | set_exception_table_evt(0x820, do_fpu_state_restore); | 822 | set_exception_table_evt(0x820, do_fpu_state_restore); |
761 | #endif | 823 | #endif |
824 | |||
825 | #ifdef CONFIG_CPU_SH2 | ||
826 | set_exception_table_vec(TRAP_ADDRESS_ERROR, address_error_handler); | ||
827 | #endif | ||
828 | #ifdef CONFIG_CPU_SH2A | ||
829 | set_exception_table_vec(TRAP_DIVZERO_ERROR, do_divide_error); | ||
830 | set_exception_table_vec(TRAP_DIVOVF_ERROR, do_divide_error); | ||
831 | #endif | ||
762 | 832 | ||
763 | /* Setup VBR for boot cpu */ | 833 | /* Setup VBR for boot cpu */ |
764 | per_cpu_trap_init(); | 834 | per_cpu_trap_init(); |