diff options
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r-- | arch/mips/kernel/traps.c | 72 |
1 files changed, 46 insertions, 26 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 9fda1b8be3a7..2a932cada244 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
@@ -54,6 +54,8 @@ extern asmlinkage void handle_dbe(void); | |||
54 | extern asmlinkage void handle_sys(void); | 54 | extern asmlinkage void handle_sys(void); |
55 | extern asmlinkage void handle_bp(void); | 55 | extern asmlinkage void handle_bp(void); |
56 | extern asmlinkage void handle_ri(void); | 56 | extern asmlinkage void handle_ri(void); |
57 | extern asmlinkage void handle_ri_rdhwr_vivt(void); | ||
58 | extern asmlinkage void handle_ri_rdhwr(void); | ||
57 | extern asmlinkage void handle_cpu(void); | 59 | extern asmlinkage void handle_cpu(void); |
58 | extern asmlinkage void handle_ov(void); | 60 | extern asmlinkage void handle_ov(void); |
59 | extern asmlinkage void handle_tr(void); | 61 | extern asmlinkage void handle_tr(void); |
@@ -397,19 +399,6 @@ asmlinkage void do_be(struct pt_regs *regs) | |||
397 | force_sig(SIGBUS, current); | 399 | force_sig(SIGBUS, current); |
398 | } | 400 | } |
399 | 401 | ||
400 | static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode) | ||
401 | { | ||
402 | unsigned int __user *epc; | ||
403 | |||
404 | epc = (unsigned int __user *) regs->cp0_epc + | ||
405 | ((regs->cp0_cause & CAUSEF_BD) != 0); | ||
406 | if (!get_user(*opcode, epc)) | ||
407 | return 0; | ||
408 | |||
409 | force_sig(SIGSEGV, current); | ||
410 | return 1; | ||
411 | } | ||
412 | |||
413 | /* | 402 | /* |
414 | * ll/sc emulation | 403 | * ll/sc emulation |
415 | */ | 404 | */ |
@@ -544,8 +533,8 @@ static inline int simulate_llsc(struct pt_regs *regs) | |||
544 | { | 533 | { |
545 | unsigned int opcode; | 534 | unsigned int opcode; |
546 | 535 | ||
547 | if (unlikely(get_insn_opcode(regs, &opcode))) | 536 | if (get_user(opcode, (unsigned int __user *) exception_epc(regs))) |
548 | return -EFAULT; | 537 | goto out_sigsegv; |
549 | 538 | ||
550 | if ((opcode & OPCODE) == LL) { | 539 | if ((opcode & OPCODE) == LL) { |
551 | simulate_ll(regs, opcode); | 540 | simulate_ll(regs, opcode); |
@@ -557,6 +546,10 @@ static inline int simulate_llsc(struct pt_regs *regs) | |||
557 | } | 546 | } |
558 | 547 | ||
559 | return -EFAULT; /* Strange things going on ... */ | 548 | return -EFAULT; /* Strange things going on ... */ |
549 | |||
550 | out_sigsegv: | ||
551 | force_sig(SIGSEGV, current); | ||
552 | return -EFAULT; | ||
560 | } | 553 | } |
561 | 554 | ||
562 | /* | 555 | /* |
@@ -569,8 +562,8 @@ static inline int simulate_rdhwr(struct pt_regs *regs) | |||
569 | struct thread_info *ti = task_thread_info(current); | 562 | struct thread_info *ti = task_thread_info(current); |
570 | unsigned int opcode; | 563 | unsigned int opcode; |
571 | 564 | ||
572 | if (unlikely(get_insn_opcode(regs, &opcode))) | 565 | if (get_user(opcode, (unsigned int __user *) exception_epc(regs))) |
573 | return -EFAULT; | 566 | goto out_sigsegv; |
574 | 567 | ||
575 | if (unlikely(compute_return_epc(regs))) | 568 | if (unlikely(compute_return_epc(regs))) |
576 | return -EFAULT; | 569 | return -EFAULT; |
@@ -589,6 +582,10 @@ static inline int simulate_rdhwr(struct pt_regs *regs) | |||
589 | 582 | ||
590 | /* Not ours. */ | 583 | /* Not ours. */ |
591 | return -EFAULT; | 584 | return -EFAULT; |
585 | |||
586 | out_sigsegv: | ||
587 | force_sig(SIGSEGV, current); | ||
588 | return -EFAULT; | ||
592 | } | 589 | } |
593 | 590 | ||
594 | asmlinkage void do_ov(struct pt_regs *regs) | 591 | asmlinkage void do_ov(struct pt_regs *regs) |
@@ -672,10 +669,8 @@ asmlinkage void do_bp(struct pt_regs *regs) | |||
672 | unsigned int opcode, bcode; | 669 | unsigned int opcode, bcode; |
673 | siginfo_t info; | 670 | siginfo_t info; |
674 | 671 | ||
675 | die_if_kernel("Break instruction in kernel code", regs); | 672 | if (get_user(opcode, (unsigned int __user *) exception_epc(regs))) |
676 | 673 | goto out_sigsegv; | |
677 | if (get_insn_opcode(regs, &opcode)) | ||
678 | return; | ||
679 | 674 | ||
680 | /* | 675 | /* |
681 | * There is the ancient bug in the MIPS assemblers that the break | 676 | * There is the ancient bug in the MIPS assemblers that the break |
@@ -696,6 +691,7 @@ asmlinkage void do_bp(struct pt_regs *regs) | |||
696 | switch (bcode) { | 691 | switch (bcode) { |
697 | case BRK_OVERFLOW << 10: | 692 | case BRK_OVERFLOW << 10: |
698 | case BRK_DIVZERO << 10: | 693 | case BRK_DIVZERO << 10: |
694 | die_if_kernel("Break instruction in kernel code", regs); | ||
699 | if (bcode == (BRK_DIVZERO << 10)) | 695 | if (bcode == (BRK_DIVZERO << 10)) |
700 | info.si_code = FPE_INTDIV; | 696 | info.si_code = FPE_INTDIV; |
701 | else | 697 | else |
@@ -705,9 +701,16 @@ asmlinkage void do_bp(struct pt_regs *regs) | |||
705 | info.si_addr = (void __user *) regs->cp0_epc; | 701 | info.si_addr = (void __user *) regs->cp0_epc; |
706 | force_sig_info(SIGFPE, &info, current); | 702 | force_sig_info(SIGFPE, &info, current); |
707 | break; | 703 | break; |
704 | case BRK_BUG: | ||
705 | die("Kernel bug detected", regs); | ||
706 | break; | ||
708 | default: | 707 | default: |
708 | die_if_kernel("Break instruction in kernel code", regs); | ||
709 | force_sig(SIGTRAP, current); | 709 | force_sig(SIGTRAP, current); |
710 | } | 710 | } |
711 | |||
712 | out_sigsegv: | ||
713 | force_sig(SIGSEGV, current); | ||
711 | } | 714 | } |
712 | 715 | ||
713 | asmlinkage void do_tr(struct pt_regs *regs) | 716 | asmlinkage void do_tr(struct pt_regs *regs) |
@@ -715,10 +718,8 @@ asmlinkage void do_tr(struct pt_regs *regs) | |||
715 | unsigned int opcode, tcode = 0; | 718 | unsigned int opcode, tcode = 0; |
716 | siginfo_t info; | 719 | siginfo_t info; |
717 | 720 | ||
718 | die_if_kernel("Trap instruction in kernel code", regs); | 721 | if (get_user(opcode, (unsigned int __user *) exception_epc(regs))) |
719 | 722 | goto out_sigsegv; | |
720 | if (get_insn_opcode(regs, &opcode)) | ||
721 | return; | ||
722 | 723 | ||
723 | /* Immediate versions don't provide a code. */ | 724 | /* Immediate versions don't provide a code. */ |
724 | if (!(opcode & OPCODE)) | 725 | if (!(opcode & OPCODE)) |
@@ -733,6 +734,7 @@ asmlinkage void do_tr(struct pt_regs *regs) | |||
733 | switch (tcode) { | 734 | switch (tcode) { |
734 | case BRK_OVERFLOW: | 735 | case BRK_OVERFLOW: |
735 | case BRK_DIVZERO: | 736 | case BRK_DIVZERO: |
737 | die_if_kernel("Trap instruction in kernel code", regs); | ||
736 | if (tcode == BRK_DIVZERO) | 738 | if (tcode == BRK_DIVZERO) |
737 | info.si_code = FPE_INTDIV; | 739 | info.si_code = FPE_INTDIV; |
738 | else | 740 | else |
@@ -742,9 +744,16 @@ asmlinkage void do_tr(struct pt_regs *regs) | |||
742 | info.si_addr = (void __user *) regs->cp0_epc; | 744 | info.si_addr = (void __user *) regs->cp0_epc; |
743 | force_sig_info(SIGFPE, &info, current); | 745 | force_sig_info(SIGFPE, &info, current); |
744 | break; | 746 | break; |
747 | case BRK_BUG: | ||
748 | die("Kernel bug detected", regs); | ||
749 | break; | ||
745 | default: | 750 | default: |
751 | die_if_kernel("Trap instruction in kernel code", regs); | ||
746 | force_sig(SIGTRAP, current); | 752 | force_sig(SIGTRAP, current); |
747 | } | 753 | } |
754 | |||
755 | out_sigsegv: | ||
756 | force_sig(SIGSEGV, current); | ||
748 | } | 757 | } |
749 | 758 | ||
750 | asmlinkage void do_ri(struct pt_regs *regs) | 759 | asmlinkage void do_ri(struct pt_regs *regs) |
@@ -1423,6 +1432,15 @@ void __init set_uncached_handler (unsigned long offset, void *addr, unsigned lon | |||
1423 | memcpy((void *)(uncached_ebase + offset), addr, size); | 1432 | memcpy((void *)(uncached_ebase + offset), addr, size); |
1424 | } | 1433 | } |
1425 | 1434 | ||
1435 | static int __initdata rdhwr_noopt; | ||
1436 | static int __init set_rdhwr_noopt(char *str) | ||
1437 | { | ||
1438 | rdhwr_noopt = 1; | ||
1439 | return 1; | ||
1440 | } | ||
1441 | |||
1442 | __setup("rdhwr_noopt", set_rdhwr_noopt); | ||
1443 | |||
1426 | void __init trap_init(void) | 1444 | void __init trap_init(void) |
1427 | { | 1445 | { |
1428 | extern char except_vec3_generic, except_vec3_r4000; | 1446 | extern char except_vec3_generic, except_vec3_r4000; |
@@ -1502,7 +1520,9 @@ void __init trap_init(void) | |||
1502 | 1520 | ||
1503 | set_except_vector(8, handle_sys); | 1521 | set_except_vector(8, handle_sys); |
1504 | set_except_vector(9, handle_bp); | 1522 | set_except_vector(9, handle_bp); |
1505 | set_except_vector(10, handle_ri); | 1523 | set_except_vector(10, rdhwr_noopt ? handle_ri : |
1524 | (cpu_has_vtag_icache ? | ||
1525 | handle_ri_rdhwr_vivt : handle_ri_rdhwr)); | ||
1506 | set_except_vector(11, handle_cpu); | 1526 | set_except_vector(11, handle_cpu); |
1507 | set_except_vector(12, handle_ov); | 1527 | set_except_vector(12, handle_ov); |
1508 | set_except_vector(13, handle_tr); | 1528 | set_except_vector(13, handle_tr); |