diff options
Diffstat (limited to 'drivers/tty/sysrq.c')
-rw-r--r-- | drivers/tty/sysrq.c | 811 |
1 files changed, 811 insertions, 0 deletions
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c new file mode 100644 index 000000000000..eaa5d3efa79d --- /dev/null +++ b/drivers/tty/sysrq.c | |||
@@ -0,0 +1,811 @@ | |||
1 | /* | ||
2 | * Linux Magic System Request Key Hacks | ||
3 | * | ||
4 | * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> | ||
5 | * based on ideas by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz> | ||
6 | * | ||
7 | * (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com> | ||
8 | * overhauled to use key registration | ||
9 | * based upon discusions in irc://irc.openprojects.net/#kernelnewbies | ||
10 | * | ||
11 | * Copyright (c) 2010 Dmitry Torokhov | ||
12 | * Input handler conversion | ||
13 | */ | ||
14 | |||
15 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
16 | |||
17 | #include <linux/sched.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/mount.h> | ||
22 | #include <linux/kdev_t.h> | ||
23 | #include <linux/major.h> | ||
24 | #include <linux/reboot.h> | ||
25 | #include <linux/sysrq.h> | ||
26 | #include <linux/kbd_kern.h> | ||
27 | #include <linux/proc_fs.h> | ||
28 | #include <linux/nmi.h> | ||
29 | #include <linux/quotaops.h> | ||
30 | #include <linux/perf_event.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/suspend.h> | ||
34 | #include <linux/writeback.h> | ||
35 | #include <linux/buffer_head.h> /* for fsync_bdev() */ | ||
36 | #include <linux/swap.h> | ||
37 | #include <linux/spinlock.h> | ||
38 | #include <linux/vt_kern.h> | ||
39 | #include <linux/workqueue.h> | ||
40 | #include <linux/hrtimer.h> | ||
41 | #include <linux/oom.h> | ||
42 | #include <linux/slab.h> | ||
43 | #include <linux/input.h> | ||
44 | |||
45 | #include <asm/ptrace.h> | ||
46 | #include <asm/irq_regs.h> | ||
47 | |||
48 | /* Whether we react on sysrq keys or just ignore them */ | ||
49 | static int __read_mostly sysrq_enabled = 1; | ||
50 | static bool __read_mostly sysrq_always_enabled; | ||
51 | |||
52 | static bool sysrq_on(void) | ||
53 | { | ||
54 | return sysrq_enabled || sysrq_always_enabled; | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * A value of 1 means 'all', other nonzero values are an op mask: | ||
59 | */ | ||
60 | static bool sysrq_on_mask(int mask) | ||
61 | { | ||
62 | return sysrq_always_enabled || | ||
63 | sysrq_enabled == 1 || | ||
64 | (sysrq_enabled & mask); | ||
65 | } | ||
66 | |||
67 | static int __init sysrq_always_enabled_setup(char *str) | ||
68 | { | ||
69 | sysrq_always_enabled = true; | ||
70 | pr_info("sysrq always enabled.\n"); | ||
71 | |||
72 | return 1; | ||
73 | } | ||
74 | |||
75 | __setup("sysrq_always_enabled", sysrq_always_enabled_setup); | ||
76 | |||
77 | |||
78 | static void sysrq_handle_loglevel(int key) | ||
79 | { | ||
80 | int i; | ||
81 | |||
82 | i = key - '0'; | ||
83 | console_loglevel = 7; | ||
84 | printk("Loglevel set to %d\n", i); | ||
85 | console_loglevel = i; | ||
86 | } | ||
87 | static struct sysrq_key_op sysrq_loglevel_op = { | ||
88 | .handler = sysrq_handle_loglevel, | ||
89 | .help_msg = "loglevel(0-9)", | ||
90 | .action_msg = "Changing Loglevel", | ||
91 | .enable_mask = SYSRQ_ENABLE_LOG, | ||
92 | }; | ||
93 | |||
94 | #ifdef CONFIG_VT | ||
95 | static void sysrq_handle_SAK(int key) | ||
96 | { | ||
97 | struct work_struct *SAK_work = &vc_cons[fg_console].SAK_work; | ||
98 | schedule_work(SAK_work); | ||
99 | } | ||
100 | static struct sysrq_key_op sysrq_SAK_op = { | ||
101 | .handler = sysrq_handle_SAK, | ||
102 | .help_msg = "saK", | ||
103 | .action_msg = "SAK", | ||
104 | .enable_mask = SYSRQ_ENABLE_KEYBOARD, | ||
105 | }; | ||
106 | #else | ||
107 | #define sysrq_SAK_op (*(struct sysrq_key_op *)NULL) | ||
108 | #endif | ||
109 | |||
110 | #ifdef CONFIG_VT | ||
111 | static void sysrq_handle_unraw(int key) | ||
112 | { | ||
113 | struct kbd_struct *kbd = &kbd_table[fg_console]; | ||
114 | |||
115 | if (kbd) | ||
116 | kbd->kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; | ||
117 | } | ||
118 | static struct sysrq_key_op sysrq_unraw_op = { | ||
119 | .handler = sysrq_handle_unraw, | ||
120 | .help_msg = "unRaw", | ||
121 | .action_msg = "Keyboard mode set to system default", | ||
122 | .enable_mask = SYSRQ_ENABLE_KEYBOARD, | ||
123 | }; | ||
124 | #else | ||
125 | #define sysrq_unraw_op (*(struct sysrq_key_op *)NULL) | ||
126 | #endif /* CONFIG_VT */ | ||
127 | |||
128 | static void sysrq_handle_crash(int key) | ||
129 | { | ||
130 | char *killer = NULL; | ||
131 | |||
132 | panic_on_oops = 1; /* force panic */ | ||
133 | wmb(); | ||
134 | *killer = 1; | ||
135 | } | ||
136 | static struct sysrq_key_op sysrq_crash_op = { | ||
137 | .handler = sysrq_handle_crash, | ||
138 | .help_msg = "Crash", | ||
139 | .action_msg = "Trigger a crash", | ||
140 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
141 | }; | ||
142 | |||
143 | static void sysrq_handle_reboot(int key) | ||
144 | { | ||
145 | lockdep_off(); | ||
146 | local_irq_enable(); | ||
147 | emergency_restart(); | ||
148 | } | ||
149 | static struct sysrq_key_op sysrq_reboot_op = { | ||
150 | .handler = sysrq_handle_reboot, | ||
151 | .help_msg = "reBoot", | ||
152 | .action_msg = "Resetting", | ||
153 | .enable_mask = SYSRQ_ENABLE_BOOT, | ||
154 | }; | ||
155 | |||
156 | static void sysrq_handle_sync(int key) | ||
157 | { | ||
158 | emergency_sync(); | ||
159 | } | ||
160 | static struct sysrq_key_op sysrq_sync_op = { | ||
161 | .handler = sysrq_handle_sync, | ||
162 | .help_msg = "Sync", | ||
163 | .action_msg = "Emergency Sync", | ||
164 | .enable_mask = SYSRQ_ENABLE_SYNC, | ||
165 | }; | ||
166 | |||
167 | static void sysrq_handle_show_timers(int key) | ||
168 | { | ||
169 | sysrq_timer_list_show(); | ||
170 | } | ||
171 | |||
172 | static struct sysrq_key_op sysrq_show_timers_op = { | ||
173 | .handler = sysrq_handle_show_timers, | ||
174 | .help_msg = "show-all-timers(Q)", | ||
175 | .action_msg = "Show clockevent devices & pending hrtimers (no others)", | ||
176 | }; | ||
177 | |||
178 | static void sysrq_handle_mountro(int key) | ||
179 | { | ||
180 | emergency_remount(); | ||
181 | } | ||
182 | static struct sysrq_key_op sysrq_mountro_op = { | ||
183 | .handler = sysrq_handle_mountro, | ||
184 | .help_msg = "Unmount", | ||
185 | .action_msg = "Emergency Remount R/O", | ||
186 | .enable_mask = SYSRQ_ENABLE_REMOUNT, | ||
187 | }; | ||
188 | |||
189 | #ifdef CONFIG_LOCKDEP | ||
190 | static void sysrq_handle_showlocks(int key) | ||
191 | { | ||
192 | debug_show_all_locks(); | ||
193 | } | ||
194 | |||
195 | static struct sysrq_key_op sysrq_showlocks_op = { | ||
196 | .handler = sysrq_handle_showlocks, | ||
197 | .help_msg = "show-all-locks(D)", | ||
198 | .action_msg = "Show Locks Held", | ||
199 | }; | ||
200 | #else | ||
201 | #define sysrq_showlocks_op (*(struct sysrq_key_op *)NULL) | ||
202 | #endif | ||
203 | |||
204 | #ifdef CONFIG_SMP | ||
205 | static DEFINE_SPINLOCK(show_lock); | ||
206 | |||
207 | static void showacpu(void *dummy) | ||
208 | { | ||
209 | unsigned long flags; | ||
210 | |||
211 | /* Idle CPUs have no interesting backtrace. */ | ||
212 | if (idle_cpu(smp_processor_id())) | ||
213 | return; | ||
214 | |||
215 | spin_lock_irqsave(&show_lock, flags); | ||
216 | printk(KERN_INFO "CPU%d:\n", smp_processor_id()); | ||
217 | show_stack(NULL, NULL); | ||
218 | spin_unlock_irqrestore(&show_lock, flags); | ||
219 | } | ||
220 | |||
221 | static void sysrq_showregs_othercpus(struct work_struct *dummy) | ||
222 | { | ||
223 | smp_call_function(showacpu, NULL, 0); | ||
224 | } | ||
225 | |||
226 | static DECLARE_WORK(sysrq_showallcpus, sysrq_showregs_othercpus); | ||
227 | |||
228 | static void sysrq_handle_showallcpus(int key) | ||
229 | { | ||
230 | /* | ||
231 | * Fall back to the workqueue based printing if the | ||
232 | * backtrace printing did not succeed or the | ||
233 | * architecture has no support for it: | ||
234 | */ | ||
235 | if (!trigger_all_cpu_backtrace()) { | ||
236 | struct pt_regs *regs = get_irq_regs(); | ||
237 | |||
238 | if (regs) { | ||
239 | printk(KERN_INFO "CPU%d:\n", smp_processor_id()); | ||
240 | show_regs(regs); | ||
241 | } | ||
242 | schedule_work(&sysrq_showallcpus); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | static struct sysrq_key_op sysrq_showallcpus_op = { | ||
247 | .handler = sysrq_handle_showallcpus, | ||
248 | .help_msg = "show-backtrace-all-active-cpus(L)", | ||
249 | .action_msg = "Show backtrace of all active CPUs", | ||
250 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
251 | }; | ||
252 | #endif | ||
253 | |||
254 | static void sysrq_handle_showregs(int key) | ||
255 | { | ||
256 | struct pt_regs *regs = get_irq_regs(); | ||
257 | if (regs) | ||
258 | show_regs(regs); | ||
259 | perf_event_print_debug(); | ||
260 | } | ||
261 | static struct sysrq_key_op sysrq_showregs_op = { | ||
262 | .handler = sysrq_handle_showregs, | ||
263 | .help_msg = "show-registers(P)", | ||
264 | .action_msg = "Show Regs", | ||
265 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
266 | }; | ||
267 | |||
268 | static void sysrq_handle_showstate(int key) | ||
269 | { | ||
270 | show_state(); | ||
271 | } | ||
272 | static struct sysrq_key_op sysrq_showstate_op = { | ||
273 | .handler = sysrq_handle_showstate, | ||
274 | .help_msg = "show-task-states(T)", | ||
275 | .action_msg = "Show State", | ||
276 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
277 | }; | ||
278 | |||
279 | static void sysrq_handle_showstate_blocked(int key) | ||
280 | { | ||
281 | show_state_filter(TASK_UNINTERRUPTIBLE); | ||
282 | } | ||
283 | static struct sysrq_key_op sysrq_showstate_blocked_op = { | ||
284 | .handler = sysrq_handle_showstate_blocked, | ||
285 | .help_msg = "show-blocked-tasks(W)", | ||
286 | .action_msg = "Show Blocked State", | ||
287 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
288 | }; | ||
289 | |||
290 | #ifdef CONFIG_TRACING | ||
291 | #include <linux/ftrace.h> | ||
292 | |||
293 | static void sysrq_ftrace_dump(int key) | ||
294 | { | ||
295 | ftrace_dump(DUMP_ALL); | ||
296 | } | ||
297 | static struct sysrq_key_op sysrq_ftrace_dump_op = { | ||
298 | .handler = sysrq_ftrace_dump, | ||
299 | .help_msg = "dump-ftrace-buffer(Z)", | ||
300 | .action_msg = "Dump ftrace buffer", | ||
301 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
302 | }; | ||
303 | #else | ||
304 | #define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)NULL) | ||
305 | #endif | ||
306 | |||
307 | static void sysrq_handle_showmem(int key) | ||
308 | { | ||
309 | show_mem(); | ||
310 | } | ||
311 | static struct sysrq_key_op sysrq_showmem_op = { | ||
312 | .handler = sysrq_handle_showmem, | ||
313 | .help_msg = "show-memory-usage(M)", | ||
314 | .action_msg = "Show Memory", | ||
315 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
316 | }; | ||
317 | |||
318 | /* | ||
319 | * Signal sysrq helper function. Sends a signal to all user processes. | ||
320 | */ | ||
321 | static void send_sig_all(int sig) | ||
322 | { | ||
323 | struct task_struct *p; | ||
324 | |||
325 | for_each_process(p) { | ||
326 | if (p->mm && !is_global_init(p)) | ||
327 | /* Not swapper, init nor kernel thread */ | ||
328 | force_sig(sig, p); | ||
329 | } | ||
330 | } | ||
331 | |||
332 | static void sysrq_handle_term(int key) | ||
333 | { | ||
334 | send_sig_all(SIGTERM); | ||
335 | console_loglevel = 8; | ||
336 | } | ||
337 | static struct sysrq_key_op sysrq_term_op = { | ||
338 | .handler = sysrq_handle_term, | ||
339 | .help_msg = "terminate-all-tasks(E)", | ||
340 | .action_msg = "Terminate All Tasks", | ||
341 | .enable_mask = SYSRQ_ENABLE_SIGNAL, | ||
342 | }; | ||
343 | |||
344 | static void moom_callback(struct work_struct *ignored) | ||
345 | { | ||
346 | out_of_memory(node_zonelist(0, GFP_KERNEL), GFP_KERNEL, 0, NULL); | ||
347 | } | ||
348 | |||
349 | static DECLARE_WORK(moom_work, moom_callback); | ||
350 | |||
351 | static void sysrq_handle_moom(int key) | ||
352 | { | ||
353 | schedule_work(&moom_work); | ||
354 | } | ||
355 | static struct sysrq_key_op sysrq_moom_op = { | ||
356 | .handler = sysrq_handle_moom, | ||
357 | .help_msg = "memory-full-oom-kill(F)", | ||
358 | .action_msg = "Manual OOM execution", | ||
359 | .enable_mask = SYSRQ_ENABLE_SIGNAL, | ||
360 | }; | ||
361 | |||
362 | #ifdef CONFIG_BLOCK | ||
363 | static void sysrq_handle_thaw(int key) | ||
364 | { | ||
365 | emergency_thaw_all(); | ||
366 | } | ||
367 | static struct sysrq_key_op sysrq_thaw_op = { | ||
368 | .handler = sysrq_handle_thaw, | ||
369 | .help_msg = "thaw-filesystems(J)", | ||
370 | .action_msg = "Emergency Thaw of all frozen filesystems", | ||
371 | .enable_mask = SYSRQ_ENABLE_SIGNAL, | ||
372 | }; | ||
373 | #endif | ||
374 | |||
375 | static void sysrq_handle_kill(int key) | ||
376 | { | ||
377 | send_sig_all(SIGKILL); | ||
378 | console_loglevel = 8; | ||
379 | } | ||
380 | static struct sysrq_key_op sysrq_kill_op = { | ||
381 | .handler = sysrq_handle_kill, | ||
382 | .help_msg = "kill-all-tasks(I)", | ||
383 | .action_msg = "Kill All Tasks", | ||
384 | .enable_mask = SYSRQ_ENABLE_SIGNAL, | ||
385 | }; | ||
386 | |||
387 | static void sysrq_handle_unrt(int key) | ||
388 | { | ||
389 | normalize_rt_tasks(); | ||
390 | } | ||
391 | static struct sysrq_key_op sysrq_unrt_op = { | ||
392 | .handler = sysrq_handle_unrt, | ||
393 | .help_msg = "nice-all-RT-tasks(N)", | ||
394 | .action_msg = "Nice All RT Tasks", | ||
395 | .enable_mask = SYSRQ_ENABLE_RTNICE, | ||
396 | }; | ||
397 | |||
398 | /* Key Operations table and lock */ | ||
399 | static DEFINE_SPINLOCK(sysrq_key_table_lock); | ||
400 | |||
401 | static struct sysrq_key_op *sysrq_key_table[36] = { | ||
402 | &sysrq_loglevel_op, /* 0 */ | ||
403 | &sysrq_loglevel_op, /* 1 */ | ||
404 | &sysrq_loglevel_op, /* 2 */ | ||
405 | &sysrq_loglevel_op, /* 3 */ | ||
406 | &sysrq_loglevel_op, /* 4 */ | ||
407 | &sysrq_loglevel_op, /* 5 */ | ||
408 | &sysrq_loglevel_op, /* 6 */ | ||
409 | &sysrq_loglevel_op, /* 7 */ | ||
410 | &sysrq_loglevel_op, /* 8 */ | ||
411 | &sysrq_loglevel_op, /* 9 */ | ||
412 | |||
413 | /* | ||
414 | * a: Don't use for system provided sysrqs, it is handled specially on | ||
415 | * sparc and will never arrive. | ||
416 | */ | ||
417 | NULL, /* a */ | ||
418 | &sysrq_reboot_op, /* b */ | ||
419 | &sysrq_crash_op, /* c & ibm_emac driver debug */ | ||
420 | &sysrq_showlocks_op, /* d */ | ||
421 | &sysrq_term_op, /* e */ | ||
422 | &sysrq_moom_op, /* f */ | ||
423 | /* g: May be registered for the kernel debugger */ | ||
424 | NULL, /* g */ | ||
425 | NULL, /* h - reserved for help */ | ||
426 | &sysrq_kill_op, /* i */ | ||
427 | #ifdef CONFIG_BLOCK | ||
428 | &sysrq_thaw_op, /* j */ | ||
429 | #else | ||
430 | NULL, /* j */ | ||
431 | #endif | ||
432 | &sysrq_SAK_op, /* k */ | ||
433 | #ifdef CONFIG_SMP | ||
434 | &sysrq_showallcpus_op, /* l */ | ||
435 | #else | ||
436 | NULL, /* l */ | ||
437 | #endif | ||
438 | &sysrq_showmem_op, /* m */ | ||
439 | &sysrq_unrt_op, /* n */ | ||
440 | /* o: This will often be registered as 'Off' at init time */ | ||
441 | NULL, /* o */ | ||
442 | &sysrq_showregs_op, /* p */ | ||
443 | &sysrq_show_timers_op, /* q */ | ||
444 | &sysrq_unraw_op, /* r */ | ||
445 | &sysrq_sync_op, /* s */ | ||
446 | &sysrq_showstate_op, /* t */ | ||
447 | &sysrq_mountro_op, /* u */ | ||
448 | /* v: May be registered for frame buffer console restore */ | ||
449 | NULL, /* v */ | ||
450 | &sysrq_showstate_blocked_op, /* w */ | ||
451 | /* x: May be registered on ppc/powerpc for xmon */ | ||
452 | NULL, /* x */ | ||
453 | /* y: May be registered on sparc64 for global register dump */ | ||
454 | NULL, /* y */ | ||
455 | &sysrq_ftrace_dump_op, /* z */ | ||
456 | }; | ||
457 | |||
458 | /* key2index calculation, -1 on invalid index */ | ||
459 | static int sysrq_key_table_key2index(int key) | ||
460 | { | ||
461 | int retval; | ||
462 | |||
463 | if ((key >= '0') && (key <= '9')) | ||
464 | retval = key - '0'; | ||
465 | else if ((key >= 'a') && (key <= 'z')) | ||
466 | retval = key + 10 - 'a'; | ||
467 | else | ||
468 | retval = -1; | ||
469 | return retval; | ||
470 | } | ||
471 | |||
472 | /* | ||
473 | * get and put functions for the table, exposed to modules. | ||
474 | */ | ||
475 | struct sysrq_key_op *__sysrq_get_key_op(int key) | ||
476 | { | ||
477 | struct sysrq_key_op *op_p = NULL; | ||
478 | int i; | ||
479 | |||
480 | i = sysrq_key_table_key2index(key); | ||
481 | if (i != -1) | ||
482 | op_p = sysrq_key_table[i]; | ||
483 | |||
484 | return op_p; | ||
485 | } | ||
486 | |||
487 | static void __sysrq_put_key_op(int key, struct sysrq_key_op *op_p) | ||
488 | { | ||
489 | int i = sysrq_key_table_key2index(key); | ||
490 | |||
491 | if (i != -1) | ||
492 | sysrq_key_table[i] = op_p; | ||
493 | } | ||
494 | |||
495 | void __handle_sysrq(int key, bool check_mask) | ||
496 | { | ||
497 | struct sysrq_key_op *op_p; | ||
498 | int orig_log_level; | ||
499 | int i; | ||
500 | unsigned long flags; | ||
501 | |||
502 | spin_lock_irqsave(&sysrq_key_table_lock, flags); | ||
503 | /* | ||
504 | * Raise the apparent loglevel to maximum so that the sysrq header | ||
505 | * is shown to provide the user with positive feedback. We do not | ||
506 | * simply emit this at KERN_EMERG as that would change message | ||
507 | * routing in the consumers of /proc/kmsg. | ||
508 | */ | ||
509 | orig_log_level = console_loglevel; | ||
510 | console_loglevel = 7; | ||
511 | printk(KERN_INFO "SysRq : "); | ||
512 | |||
513 | op_p = __sysrq_get_key_op(key); | ||
514 | if (op_p) { | ||
515 | /* | ||
516 | * Should we check for enabled operations (/proc/sysrq-trigger | ||
517 | * should not) and is the invoked operation enabled? | ||
518 | */ | ||
519 | if (!check_mask || sysrq_on_mask(op_p->enable_mask)) { | ||
520 | printk("%s\n", op_p->action_msg); | ||
521 | console_loglevel = orig_log_level; | ||
522 | op_p->handler(key); | ||
523 | } else { | ||
524 | printk("This sysrq operation is disabled.\n"); | ||
525 | } | ||
526 | } else { | ||
527 | printk("HELP : "); | ||
528 | /* Only print the help msg once per handler */ | ||
529 | for (i = 0; i < ARRAY_SIZE(sysrq_key_table); i++) { | ||
530 | if (sysrq_key_table[i]) { | ||
531 | int j; | ||
532 | |||
533 | for (j = 0; sysrq_key_table[i] != | ||
534 | sysrq_key_table[j]; j++) | ||
535 | ; | ||
536 | if (j != i) | ||
537 | continue; | ||
538 | printk("%s ", sysrq_key_table[i]->help_msg); | ||
539 | } | ||
540 | } | ||
541 | printk("\n"); | ||
542 | console_loglevel = orig_log_level; | ||
543 | } | ||
544 | spin_unlock_irqrestore(&sysrq_key_table_lock, flags); | ||
545 | } | ||
546 | |||
547 | void handle_sysrq(int key) | ||
548 | { | ||
549 | if (sysrq_on()) | ||
550 | __handle_sysrq(key, true); | ||
551 | } | ||
552 | EXPORT_SYMBOL(handle_sysrq); | ||
553 | |||
554 | #ifdef CONFIG_INPUT | ||
555 | |||
556 | /* Simple translation table for the SysRq keys */ | ||
557 | static const unsigned char sysrq_xlate[KEY_MAX + 1] = | ||
558 | "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ | ||
559 | "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ | ||
560 | "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ | ||
561 | "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ | ||
562 | "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ | ||
563 | "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ | ||
564 | "\r\000/"; /* 0x60 - 0x6f */ | ||
565 | |||
566 | static bool sysrq_down; | ||
567 | static int sysrq_alt_use; | ||
568 | static int sysrq_alt; | ||
569 | static DEFINE_SPINLOCK(sysrq_event_lock); | ||
570 | |||
571 | static bool sysrq_filter(struct input_handle *handle, unsigned int type, | ||
572 | unsigned int code, int value) | ||
573 | { | ||
574 | bool suppress; | ||
575 | |||
576 | /* We are called with interrupts disabled, just take the lock */ | ||
577 | spin_lock(&sysrq_event_lock); | ||
578 | |||
579 | if (type != EV_KEY) | ||
580 | goto out; | ||
581 | |||
582 | switch (code) { | ||
583 | |||
584 | case KEY_LEFTALT: | ||
585 | case KEY_RIGHTALT: | ||
586 | if (value) | ||
587 | sysrq_alt = code; | ||
588 | else { | ||
589 | if (sysrq_down && code == sysrq_alt_use) | ||
590 | sysrq_down = false; | ||
591 | |||
592 | sysrq_alt = 0; | ||
593 | } | ||
594 | break; | ||
595 | |||
596 | case KEY_SYSRQ: | ||
597 | if (value == 1 && sysrq_alt) { | ||
598 | sysrq_down = true; | ||
599 | sysrq_alt_use = sysrq_alt; | ||
600 | } | ||
601 | break; | ||
602 | |||
603 | default: | ||
604 | if (sysrq_down && value && value != 2) | ||
605 | __handle_sysrq(sysrq_xlate[code], true); | ||
606 | break; | ||
607 | } | ||
608 | |||
609 | out: | ||
610 | suppress = sysrq_down; | ||
611 | spin_unlock(&sysrq_event_lock); | ||
612 | |||
613 | return suppress; | ||
614 | } | ||
615 | |||
616 | static int sysrq_connect(struct input_handler *handler, | ||
617 | struct input_dev *dev, | ||
618 | const struct input_device_id *id) | ||
619 | { | ||
620 | struct input_handle *handle; | ||
621 | int error; | ||
622 | |||
623 | sysrq_down = false; | ||
624 | sysrq_alt = 0; | ||
625 | |||
626 | handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); | ||
627 | if (!handle) | ||
628 | return -ENOMEM; | ||
629 | |||
630 | handle->dev = dev; | ||
631 | handle->handler = handler; | ||
632 | handle->name = "sysrq"; | ||
633 | |||
634 | error = input_register_handle(handle); | ||
635 | if (error) { | ||
636 | pr_err("Failed to register input sysrq handler, error %d\n", | ||
637 | error); | ||
638 | goto err_free; | ||
639 | } | ||
640 | |||
641 | error = input_open_device(handle); | ||
642 | if (error) { | ||
643 | pr_err("Failed to open input device, error %d\n", error); | ||
644 | goto err_unregister; | ||
645 | } | ||
646 | |||
647 | return 0; | ||
648 | |||
649 | err_unregister: | ||
650 | input_unregister_handle(handle); | ||
651 | err_free: | ||
652 | kfree(handle); | ||
653 | return error; | ||
654 | } | ||
655 | |||
656 | static void sysrq_disconnect(struct input_handle *handle) | ||
657 | { | ||
658 | input_close_device(handle); | ||
659 | input_unregister_handle(handle); | ||
660 | kfree(handle); | ||
661 | } | ||
662 | |||
663 | /* | ||
664 | * We are matching on KEY_LEFTALT instead of KEY_SYSRQ because not all | ||
665 | * keyboards have SysRq key predefined and so user may add it to keymap | ||
666 | * later, but we expect all such keyboards to have left alt. | ||
667 | */ | ||
668 | static const struct input_device_id sysrq_ids[] = { | ||
669 | { | ||
670 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | | ||
671 | INPUT_DEVICE_ID_MATCH_KEYBIT, | ||
672 | .evbit = { BIT_MASK(EV_KEY) }, | ||
673 | .keybit = { BIT_MASK(KEY_LEFTALT) }, | ||
674 | }, | ||
675 | { }, | ||
676 | }; | ||
677 | |||
678 | static struct input_handler sysrq_handler = { | ||
679 | .filter = sysrq_filter, | ||
680 | .connect = sysrq_connect, | ||
681 | .disconnect = sysrq_disconnect, | ||
682 | .name = "sysrq", | ||
683 | .id_table = sysrq_ids, | ||
684 | }; | ||
685 | |||
686 | static bool sysrq_handler_registered; | ||
687 | |||
688 | static inline void sysrq_register_handler(void) | ||
689 | { | ||
690 | int error; | ||
691 | |||
692 | error = input_register_handler(&sysrq_handler); | ||
693 | if (error) | ||
694 | pr_err("Failed to register input handler, error %d", error); | ||
695 | else | ||
696 | sysrq_handler_registered = true; | ||
697 | } | ||
698 | |||
699 | static inline void sysrq_unregister_handler(void) | ||
700 | { | ||
701 | if (sysrq_handler_registered) { | ||
702 | input_unregister_handler(&sysrq_handler); | ||
703 | sysrq_handler_registered = false; | ||
704 | } | ||
705 | } | ||
706 | |||
707 | #else | ||
708 | |||
709 | static inline void sysrq_register_handler(void) | ||
710 | { | ||
711 | } | ||
712 | |||
713 | static inline void sysrq_unregister_handler(void) | ||
714 | { | ||
715 | } | ||
716 | |||
717 | #endif /* CONFIG_INPUT */ | ||
718 | |||
719 | int sysrq_toggle_support(int enable_mask) | ||
720 | { | ||
721 | bool was_enabled = sysrq_on(); | ||
722 | |||
723 | sysrq_enabled = enable_mask; | ||
724 | |||
725 | if (was_enabled != sysrq_on()) { | ||
726 | if (sysrq_on()) | ||
727 | sysrq_register_handler(); | ||
728 | else | ||
729 | sysrq_unregister_handler(); | ||
730 | } | ||
731 | |||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, | ||
736 | struct sysrq_key_op *remove_op_p) | ||
737 | { | ||
738 | int retval; | ||
739 | unsigned long flags; | ||
740 | |||
741 | spin_lock_irqsave(&sysrq_key_table_lock, flags); | ||
742 | if (__sysrq_get_key_op(key) == remove_op_p) { | ||
743 | __sysrq_put_key_op(key, insert_op_p); | ||
744 | retval = 0; | ||
745 | } else { | ||
746 | retval = -1; | ||
747 | } | ||
748 | spin_unlock_irqrestore(&sysrq_key_table_lock, flags); | ||
749 | return retval; | ||
750 | } | ||
751 | |||
752 | int register_sysrq_key(int key, struct sysrq_key_op *op_p) | ||
753 | { | ||
754 | return __sysrq_swap_key_ops(key, op_p, NULL); | ||
755 | } | ||
756 | EXPORT_SYMBOL(register_sysrq_key); | ||
757 | |||
758 | int unregister_sysrq_key(int key, struct sysrq_key_op *op_p) | ||
759 | { | ||
760 | return __sysrq_swap_key_ops(key, NULL, op_p); | ||
761 | } | ||
762 | EXPORT_SYMBOL(unregister_sysrq_key); | ||
763 | |||
764 | #ifdef CONFIG_PROC_FS | ||
765 | /* | ||
766 | * writing 'C' to /proc/sysrq-trigger is like sysrq-C | ||
767 | */ | ||
768 | static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf, | ||
769 | size_t count, loff_t *ppos) | ||
770 | { | ||
771 | if (count) { | ||
772 | char c; | ||
773 | |||
774 | if (get_user(c, buf)) | ||
775 | return -EFAULT; | ||
776 | __handle_sysrq(c, false); | ||
777 | } | ||
778 | |||
779 | return count; | ||
780 | } | ||
781 | |||
782 | static const struct file_operations proc_sysrq_trigger_operations = { | ||
783 | .write = write_sysrq_trigger, | ||
784 | .llseek = noop_llseek, | ||
785 | }; | ||
786 | |||
787 | static void sysrq_init_procfs(void) | ||
788 | { | ||
789 | if (!proc_create("sysrq-trigger", S_IWUSR, NULL, | ||
790 | &proc_sysrq_trigger_operations)) | ||
791 | pr_err("Failed to register proc interface\n"); | ||
792 | } | ||
793 | |||
794 | #else | ||
795 | |||
796 | static inline void sysrq_init_procfs(void) | ||
797 | { | ||
798 | } | ||
799 | |||
800 | #endif /* CONFIG_PROC_FS */ | ||
801 | |||
802 | static int __init sysrq_init(void) | ||
803 | { | ||
804 | sysrq_init_procfs(); | ||
805 | |||
806 | if (sysrq_on()) | ||
807 | sysrq_register_handler(); | ||
808 | |||
809 | return 0; | ||
810 | } | ||
811 | module_init(sysrq_init); | ||