diff options
Diffstat (limited to 'drivers/tty/sysrq.c')
-rw-r--r-- | drivers/tty/sysrq.c | 276 |
1 files changed, 202 insertions, 74 deletions
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 40e5b3919e27..814655ee2d61 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <linux/slab.h> | 42 | #include <linux/slab.h> |
43 | #include <linux/input.h> | 43 | #include <linux/input.h> |
44 | #include <linux/uaccess.h> | 44 | #include <linux/uaccess.h> |
45 | #include <linux/moduleparam.h> | ||
45 | 46 | ||
46 | #include <asm/ptrace.h> | 47 | #include <asm/ptrace.h> |
47 | #include <asm/irq_regs.h> | 48 | #include <asm/irq_regs.h> |
@@ -578,8 +579,71 @@ struct sysrq_state { | |||
578 | bool active; | 579 | bool active; |
579 | bool need_reinject; | 580 | bool need_reinject; |
580 | bool reinjecting; | 581 | bool reinjecting; |
582 | |||
583 | /* reset sequence handling */ | ||
584 | bool reset_canceled; | ||
585 | unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)]; | ||
586 | int reset_seq_len; | ||
587 | int reset_seq_cnt; | ||
588 | int reset_seq_version; | ||
581 | }; | 589 | }; |
582 | 590 | ||
591 | #define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */ | ||
592 | static unsigned short sysrq_reset_seq[SYSRQ_KEY_RESET_MAX]; | ||
593 | static unsigned int sysrq_reset_seq_len; | ||
594 | static unsigned int sysrq_reset_seq_version = 1; | ||
595 | |||
596 | static void sysrq_parse_reset_sequence(struct sysrq_state *state) | ||
597 | { | ||
598 | int i; | ||
599 | unsigned short key; | ||
600 | |||
601 | state->reset_seq_cnt = 0; | ||
602 | |||
603 | for (i = 0; i < sysrq_reset_seq_len; i++) { | ||
604 | key = sysrq_reset_seq[i]; | ||
605 | |||
606 | if (key == KEY_RESERVED || key > KEY_MAX) | ||
607 | break; | ||
608 | |||
609 | __set_bit(key, state->reset_keybit); | ||
610 | state->reset_seq_len++; | ||
611 | |||
612 | if (test_bit(key, state->key_down)) | ||
613 | state->reset_seq_cnt++; | ||
614 | } | ||
615 | |||
616 | /* Disable reset until old keys are not released */ | ||
617 | state->reset_canceled = state->reset_seq_cnt != 0; | ||
618 | |||
619 | state->reset_seq_version = sysrq_reset_seq_version; | ||
620 | } | ||
621 | |||
622 | static bool sysrq_detect_reset_sequence(struct sysrq_state *state, | ||
623 | unsigned int code, int value) | ||
624 | { | ||
625 | if (!test_bit(code, state->reset_keybit)) { | ||
626 | /* | ||
627 | * Pressing any key _not_ in reset sequence cancels | ||
628 | * the reset sequence. | ||
629 | */ | ||
630 | if (value && state->reset_seq_cnt) | ||
631 | state->reset_canceled = true; | ||
632 | } else if (value == 0) { | ||
633 | /* key release */ | ||
634 | if (--state->reset_seq_cnt == 0) | ||
635 | state->reset_canceled = false; | ||
636 | } else if (value == 1) { | ||
637 | /* key press, not autorepeat */ | ||
638 | if (++state->reset_seq_cnt == state->reset_seq_len && | ||
639 | !state->reset_canceled) { | ||
640 | return true; | ||
641 | } | ||
642 | } | ||
643 | |||
644 | return false; | ||
645 | } | ||
646 | |||
583 | static void sysrq_reinject_alt_sysrq(struct work_struct *work) | 647 | static void sysrq_reinject_alt_sysrq(struct work_struct *work) |
584 | { | 648 | { |
585 | struct sysrq_state *sysrq = | 649 | struct sysrq_state *sysrq = |
@@ -606,100 +670,121 @@ static void sysrq_reinject_alt_sysrq(struct work_struct *work) | |||
606 | } | 670 | } |
607 | } | 671 | } |
608 | 672 | ||
609 | static bool sysrq_filter(struct input_handle *handle, | 673 | static bool sysrq_handle_keypress(struct sysrq_state *sysrq, |
610 | unsigned int type, unsigned int code, int value) | 674 | unsigned int code, int value) |
611 | { | 675 | { |
612 | struct sysrq_state *sysrq = handle->private; | ||
613 | bool was_active = sysrq->active; | 676 | bool was_active = sysrq->active; |
614 | bool suppress; | 677 | bool suppress; |
615 | 678 | ||
616 | /* | 679 | switch (code) { |
617 | * Do not filter anything if we are in the process of re-injecting | ||
618 | * Alt+SysRq combination. | ||
619 | */ | ||
620 | if (sysrq->reinjecting) | ||
621 | return false; | ||
622 | 680 | ||
623 | switch (type) { | 681 | case KEY_LEFTALT: |
682 | case KEY_RIGHTALT: | ||
683 | if (!value) { | ||
684 | /* One of ALTs is being released */ | ||
685 | if (sysrq->active && code == sysrq->alt_use) | ||
686 | sysrq->active = false; | ||
624 | 687 | ||
625 | case EV_SYN: | 688 | sysrq->alt = KEY_RESERVED; |
626 | suppress = false; | 689 | |
690 | } else if (value != 2) { | ||
691 | sysrq->alt = code; | ||
692 | sysrq->need_reinject = false; | ||
693 | } | ||
627 | break; | 694 | break; |
628 | 695 | ||
629 | case EV_KEY: | 696 | case KEY_SYSRQ: |
630 | switch (code) { | 697 | if (value == 1 && sysrq->alt != KEY_RESERVED) { |
698 | sysrq->active = true; | ||
699 | sysrq->alt_use = sysrq->alt; | ||
700 | /* | ||
701 | * If nothing else will be pressed we'll need | ||
702 | * to re-inject Alt-SysRq keysroke. | ||
703 | */ | ||
704 | sysrq->need_reinject = true; | ||
705 | } | ||
631 | 706 | ||
632 | case KEY_LEFTALT: | 707 | /* |
633 | case KEY_RIGHTALT: | 708 | * Pretend that sysrq was never pressed at all. This |
634 | if (!value) { | 709 | * is needed to properly handle KGDB which will try |
635 | /* One of ALTs is being released */ | 710 | * to release all keys after exiting debugger. If we |
636 | if (sysrq->active && code == sysrq->alt_use) | 711 | * do not clear key bit it KGDB will end up sending |
637 | sysrq->active = false; | 712 | * release events for Alt and SysRq, potentially |
713 | * triggering print screen function. | ||
714 | */ | ||
715 | if (sysrq->active) | ||
716 | clear_bit(KEY_SYSRQ, sysrq->handle.dev->key); | ||
638 | 717 | ||
639 | sysrq->alt = KEY_RESERVED; | 718 | break; |
640 | 719 | ||
641 | } else if (value != 2) { | 720 | default: |
642 | sysrq->alt = code; | 721 | if (sysrq->active && value && value != 2) { |
643 | sysrq->need_reinject = false; | 722 | sysrq->need_reinject = false; |
644 | } | 723 | __handle_sysrq(sysrq_xlate[code], true); |
645 | break; | 724 | } |
725 | break; | ||
726 | } | ||
646 | 727 | ||
647 | case KEY_SYSRQ: | 728 | suppress = sysrq->active; |
648 | if (value == 1 && sysrq->alt != KEY_RESERVED) { | ||
649 | sysrq->active = true; | ||
650 | sysrq->alt_use = sysrq->alt; | ||
651 | /* | ||
652 | * If nothing else will be pressed we'll need | ||
653 | * to re-inject Alt-SysRq keysroke. | ||
654 | */ | ||
655 | sysrq->need_reinject = true; | ||
656 | } | ||
657 | 729 | ||
658 | /* | 730 | if (!sysrq->active) { |
659 | * Pretend that sysrq was never pressed at all. This | ||
660 | * is needed to properly handle KGDB which will try | ||
661 | * to release all keys after exiting debugger. If we | ||
662 | * do not clear key bit it KGDB will end up sending | ||
663 | * release events for Alt and SysRq, potentially | ||
664 | * triggering print screen function. | ||
665 | */ | ||
666 | if (sysrq->active) | ||
667 | clear_bit(KEY_SYSRQ, handle->dev->key); | ||
668 | 731 | ||
669 | break; | 732 | /* |
733 | * See if reset sequence has changed since the last time. | ||
734 | */ | ||
735 | if (sysrq->reset_seq_version != sysrq_reset_seq_version) | ||
736 | sysrq_parse_reset_sequence(sysrq); | ||
670 | 737 | ||
671 | default: | 738 | /* |
672 | if (sysrq->active && value && value != 2) { | 739 | * If we are not suppressing key presses keep track of |
673 | sysrq->need_reinject = false; | 740 | * keyboard state so we can release keys that have been |
674 | __handle_sysrq(sysrq_xlate[code], true); | 741 | * pressed before entering SysRq mode. |
675 | } | 742 | */ |
676 | break; | 743 | if (value) |
744 | set_bit(code, sysrq->key_down); | ||
745 | else | ||
746 | clear_bit(code, sysrq->key_down); | ||
747 | |||
748 | if (was_active) | ||
749 | schedule_work(&sysrq->reinject_work); | ||
750 | |||
751 | if (sysrq_detect_reset_sequence(sysrq, code, value)) { | ||
752 | /* Force emergency reboot */ | ||
753 | __handle_sysrq(sysrq_xlate[KEY_B], false); | ||
677 | } | 754 | } |
678 | 755 | ||
679 | suppress = sysrq->active; | 756 | } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { |
757 | /* | ||
758 | * Pass on release events for keys that was pressed before | ||
759 | * entering SysRq mode. | ||
760 | */ | ||
761 | suppress = false; | ||
762 | } | ||
680 | 763 | ||
681 | if (!sysrq->active) { | 764 | return suppress; |
682 | /* | 765 | } |
683 | * If we are not suppressing key presses keep track of | ||
684 | * keyboard state so we can release keys that have been | ||
685 | * pressed before entering SysRq mode. | ||
686 | */ | ||
687 | if (value) | ||
688 | set_bit(code, sysrq->key_down); | ||
689 | else | ||
690 | clear_bit(code, sysrq->key_down); | ||
691 | 766 | ||
692 | if (was_active) | 767 | static bool sysrq_filter(struct input_handle *handle, |
693 | schedule_work(&sysrq->reinject_work); | 768 | unsigned int type, unsigned int code, int value) |
769 | { | ||
770 | struct sysrq_state *sysrq = handle->private; | ||
771 | bool suppress; | ||
694 | 772 | ||
695 | } else if (value == 0 && | 773 | /* |
696 | test_and_clear_bit(code, sysrq->key_down)) { | 774 | * Do not filter anything if we are in the process of re-injecting |
697 | /* | 775 | * Alt+SysRq combination. |
698 | * Pass on release events for keys that was pressed before | 776 | */ |
699 | * entering SysRq mode. | 777 | if (sysrq->reinjecting) |
700 | */ | 778 | return false; |
701 | suppress = false; | 779 | |
702 | } | 780 | switch (type) { |
781 | |||
782 | case EV_SYN: | ||
783 | suppress = false; | ||
784 | break; | ||
785 | |||
786 | case EV_KEY: | ||
787 | suppress = sysrq_handle_keypress(sysrq, code, value); | ||
703 | break; | 788 | break; |
704 | 789 | ||
705 | default: | 790 | default: |
@@ -787,7 +872,20 @@ static bool sysrq_handler_registered; | |||
787 | 872 | ||
788 | static inline void sysrq_register_handler(void) | 873 | static inline void sysrq_register_handler(void) |
789 | { | 874 | { |
875 | extern unsigned short platform_sysrq_reset_seq[] __weak; | ||
876 | unsigned short key; | ||
790 | int error; | 877 | int error; |
878 | int i; | ||
879 | |||
880 | if (platform_sysrq_reset_seq) { | ||
881 | for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) { | ||
882 | key = platform_sysrq_reset_seq[i]; | ||
883 | if (key == KEY_RESERVED || key > KEY_MAX) | ||
884 | break; | ||
885 | |||
886 | sysrq_reset_seq[sysrq_reset_seq_len++] = key; | ||
887 | } | ||
888 | } | ||
791 | 889 | ||
792 | error = input_register_handler(&sysrq_handler); | 890 | error = input_register_handler(&sysrq_handler); |
793 | if (error) | 891 | if (error) |
@@ -804,6 +902,36 @@ static inline void sysrq_unregister_handler(void) | |||
804 | } | 902 | } |
805 | } | 903 | } |
806 | 904 | ||
905 | static int sysrq_reset_seq_param_set(const char *buffer, | ||
906 | const struct kernel_param *kp) | ||
907 | { | ||
908 | unsigned long val; | ||
909 | int error; | ||
910 | |||
911 | error = strict_strtoul(buffer, 0, &val); | ||
912 | if (error < 0) | ||
913 | return error; | ||
914 | |||
915 | if (val > KEY_MAX) | ||
916 | return -EINVAL; | ||
917 | |||
918 | *((unsigned short *)kp->arg) = val; | ||
919 | sysrq_reset_seq_version++; | ||
920 | |||
921 | return 0; | ||
922 | } | ||
923 | |||
924 | static struct kernel_param_ops param_ops_sysrq_reset_seq = { | ||
925 | .get = param_get_ushort, | ||
926 | .set = sysrq_reset_seq_param_set, | ||
927 | }; | ||
928 | |||
929 | #define param_check_sysrq_reset_seq(name, p) \ | ||
930 | __param_check(name, p, unsigned short) | ||
931 | |||
932 | module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, | ||
933 | &sysrq_reset_seq_len, 0644); | ||
934 | |||
807 | #else | 935 | #else |
808 | 936 | ||
809 | static inline void sysrq_register_handler(void) | 937 | static inline void sysrq_register_handler(void) |