| @@ -20,6 +20,7 @@ · 19 unmodified lines |
| 20 | 20 | #include <linux/device.h> |
| 21 | 21 | #include <linux/serial.h> /* for serial_state and serial_icounter_struct */ |
| 22 | 22 | #include <linux/serial_core.h> |
| 23 | +#include <linux/sysrq.h> |
| 23 | 24 | #include <linux/delay.h> |
| 24 | 25 | #include <linux/mutex.h> |
| 25 | 26 | #include <linux/security.h> |
| @@ -40,6 +41,8 @@ static struct lock_class_key port_lock_key; · 14 unmodified lines |
| 40 | 41 | |
| 41 | 42 | #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) |
| 42 | 43 | |
| 44 | +#define SYSRQ_TIMEOUT (HZ * 5) |
| 45 | + |
| 43 | 46 | static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, |
| 44 | 47 | struct ktermios *old_termios); |
| 45 | 48 | static void uart_wait_until_sent(struct tty_struct *tty, int timeout); |
| @@ -3084,6 +3087,56 @@ void uart_insert_char(struct uart_port *port, unsigned int status, · 3038 unmodified lines |
| 3084 | 3087 | } |
| 3085 | 3088 | EXPORT_SYMBOL_GPL(uart_insert_char); |
| 3086 | 3089 | |
| 3090 | +#ifdef CONFIG_MAGIC_SYSRQ_SERIAL |
| 3091 | +static const char sysrq_toggle_seq[] = CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE; |
| 3092 | + |
| 3093 | +static void uart_sysrq_on(struct work_struct *w) |
| 3094 | +{ |
| 3095 | + sysrq_toggle_support(1); |
| 3096 | + pr_info("SysRq is enabled by magic sequence on serial\n"); |
| 3097 | +} |
| 3098 | +static DECLARE_WORK(sysrq_enable_work, uart_sysrq_on); |
| 3099 | + |
| 3100 | +/** |
| 3101 | + * uart_try_toggle_sysrq - Enables SysRq from serial line |
| 3102 | + * @port: uart_port structure where char(s) after BREAK met |
| 3103 | + * @ch: new character in the sequence after received BREAK |
| 3104 | + * |
| 3105 | + * Enables magic SysRq when the required sequence is met on port |
| 3106 | + * (see CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE). |
| 3107 | + * |
| 3108 | + * Returns false if @ch is out of enabling sequence and should be |
| 3109 | + * handled some other way, true if @ch was consumed. |
| 3110 | + */ |
| 3111 | +static bool uart_try_toggle_sysrq(struct uart_port *port, unsigned int ch) |
| 3112 | +{ |
| 3113 | + if (ARRAY_SIZE(sysrq_toggle_seq) <= 1) |
| 3114 | + return false; |
| 3115 | + |
| 3116 | + BUILD_BUG_ON(ARRAY_SIZE(sysrq_toggle_seq) >= U8_MAX); |
| 3117 | + if (sysrq_toggle_seq[port->sysrq_seq] != ch) { |
| 3118 | + port->sysrq_seq = 0; |
| 3119 | + return false; |
| 3120 | + } |
| 3121 | + |
| 3122 | + /* Without the last \0 */ |
| 3123 | + if (++port->sysrq_seq < (ARRAY_SIZE(sysrq_toggle_seq) - 1)) { |
| 3124 | + port->sysrq = jiffies + SYSRQ_TIMEOUT; |
| 3125 | + return true; |
| 3126 | + } |
| 3127 | + |
| 3128 | + schedule_work(&sysrq_enable_work); |
| 3129 | + |
| 3130 | + port->sysrq = 0; |
| 3131 | + return true; |
| 3132 | +} |
| 3133 | +#else |
| 3134 | +static inline bool uart_try_toggle_sysrq(struct uart_port *port, unsigned int ch) |
| 3135 | +{ |
| 3136 | + return false; |
| 3137 | +} |
| 3138 | +#endif |
| 3139 | + |
| 3087 | 3140 | int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) |
| 3088 | 3141 | { |
| 3089 | 3142 | if (!IS_ENABLED(CONFIG_MAGIC_SYSRQ_SERIAL)) |
| @@ -3093,9 +3146,13 @@ int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) · 3 unmodified lines |
| 3093 | 3146 | return 0; |
| 3094 | 3147 | |
| 3095 | 3148 | if (ch && time_before(jiffies, port->sysrq)) { |
| 3096 | | − handle_sysrq(ch); |
| 3097 | | − port->sysrq = 0; |
| 3098 | | − return 1; |
| 3149 | + if (sysrq_mask()) { |
| 3150 | + handle_sysrq(ch); |
| 3151 | + port->sysrq = 0; |
| 3152 | + return 1; |
| 3153 | + } |
| 3154 | + if (uart_try_toggle_sysrq(port, ch)) |
| 3155 | + return 1; |
| 3099 | 3156 | } |
| 3100 | 3157 | port->sysrq = 0; |
| 3101 | 3158 | |
| @@ -3112,9 +3169,13 @@ int uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch) · 10 unmodified lines |
| 3112 | 3169 | return 0; |
| 3113 | 3170 | |
| 3114 | 3171 | if (ch && time_before(jiffies, port->sysrq)) { |
| 3115 | | − port->sysrq_ch = ch; |
| 3116 | | − port->sysrq = 0; |
| 3117 | | − return 1; |
| 3172 | + if (sysrq_mask()) { |
| 3173 | + port->sysrq_ch = ch; |
| 3174 | + port->sysrq = 0; |
| 3175 | + return 1; |
| 3176 | + } |
| 3177 | + if (uart_try_toggle_sysrq(port, ch)) |
| 3178 | + return 1; |
| 3118 | 3179 | } |
| 3119 | 3180 | port->sysrq = 0; |
| 3120 | 3181 | |
| @@ -3154,7 +3215,7 @@ int uart_handle_break(struct uart_port *port) · 33 unmodified lines |
| 3154 | 3215 | if (port->has_sysrq) { |
| 3155 | 3216 | if (port->cons && port->cons->index == port->line) { |
| 3156 | 3217 | if (!port->sysrq) { |
| 3157 | | − port->sysrq = jiffies + HZ*5; |
| 3218 | + port->sysrq = jiffies + SYSRQ_TIMEOUT; |
| 3158 | 3219 | return 1; |
| 3159 | 3220 | } |
| 3160 | 3221 | port->sysrq = 0; |