// SPDX-License-Identifier: GPL-2.0 /* * Linux Magic System Request Key Hacks * * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> * based on ideas by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz> * * (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com> * overhauled to use key registration * based upon discusions in irc://irc.openprojects.net/#kernelnewbies * * Copyright (c) 2010 Dmitry Torokhov * Input handler conversion
*/
/* Whether we react on sysrq keys or just ignore them */ staticint __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; staticbool __read_mostly sysrq_always_enabled;
/** * sysrq_mask - Getter for sysrq_enabled mask. * * Return: 1 if sysrq is always enabled, enabled sysrq_key_op mask otherwise.
*/ int sysrq_mask(void)
{ if (sysrq_always_enabled) return 1; return sysrq_enabled;
}
EXPORT_SYMBOL_GPL(sysrq_mask);
/* * A value of 1 means 'all', other nonzero values are an op mask:
*/ staticbool sysrq_on_mask(int mask)
{ return sysrq_always_enabled ||
sysrq_enabled == 1 ||
(sysrq_enabled & mask);
}
/* Idle CPUs have no interesting backtrace. */ if (idle_cpu(smp_processor_id())) {
pr_info("CPU%d: backtrace skipped as idling\n", smp_processor_id()); return;
}
staticvoid sysrq_handle_showallcpus(u8 key)
{ /* * Fall back to the workqueue based printing if the * backtrace printing did not succeed or the * architecture has no support for it:
*/ if (!trigger_all_cpu_backtrace()) { struct pt_regs *regs = NULL;
if (in_hardirq())
regs = get_irq_regs();
pr_info("CPU%d:\n", get_cpu()); if (regs)
show_regs(regs); else
show_stack(NULL, NULL, KERN_INFO);
schedule_work(&sysrq_showallcpus);
put_cpu();
}
}
staticconststruct sysrq_key_op sysrq_showallcpus_op = {
.handler = sysrq_handle_showallcpus,
.help_msg = "show-backtrace-all-active-cpus(l)",
.action_msg = "Show backtrace of all active CPUs",
.enable_mask = SYSRQ_ENABLE_DUMP,
}; #else #define sysrq_showallcpus_op (*(conststruct sysrq_key_op *)NULL) #endif
/* * a: Don't use for system provided sysrqs, it is handled specially on * sparc and will never arrive.
*/
NULL, /* a */
&sysrq_reboot_op, /* b */
&sysrq_crash_op, /* c */
&sysrq_showlocks_op, /* d */
&sysrq_term_op, /* e */
&sysrq_moom_op, /* f */ /* g: May be registered for the kernel debugger */
NULL, /* g */
NULL, /* h - reserved for help */
&sysrq_kill_op, /* i */
&sysrq_thaw_op, /* j */
&sysrq_SAK_op, /* k */
&sysrq_showallcpus_op, /* l */
&sysrq_showmem_op, /* m */
&sysrq_unrt_op, /* n */ /* o: This will often be registered as 'Off' at init time */
NULL, /* o */
&sysrq_showregs_op, /* p */
&sysrq_show_timers_op, /* q */
&sysrq_unraw_op, /* r */
&sysrq_sync_op, /* s */
&sysrq_showstate_op, /* t */
&sysrq_mountro_op, /* u */ /* v: May be registered for frame buffer console restore */
NULL, /* v */
&sysrq_showstate_blocked_op, /* w */ /* x: May be registered on mips for TLB dump */ /* x: May be registered on ppc/powerpc for xmon */ /* x: May be registered on sparc64 for global PMU dump */
NULL, /* x */ /* y: May be registered on sparc64 for global register dump */
NULL, /* y */
&sysrq_ftrace_dump_op, /* z */
NULL, /* A */
NULL, /* B */
NULL, /* C */
NULL, /* D */
NULL, /* E */
NULL, /* F */
NULL, /* G */
NULL, /* H */
NULL, /* I */
NULL, /* J */
NULL, /* K */
NULL, /* L */
NULL, /* M */
NULL, /* N */
NULL, /* O */
NULL, /* P */
NULL, /* Q */
&sysrq_replay_logs_op, /* R */ /* S: May be registered by sched_ext for resetting */
NULL, /* S */
NULL, /* T */
NULL, /* U */
NULL, /* V */
NULL, /* W */
NULL, /* X */
NULL, /* Y */
NULL, /* Z */
};
/* * get and put functions for the table, exposed to modules.
*/ staticconststruct sysrq_key_op *__sysrq_get_key_op(u8 key)
{ conststruct sysrq_key_op *op_p = NULL; int i;
i = sysrq_key_table_key2index(key); if (i != -1)
op_p = sysrq_key_table[i];
return op_p;
}
staticvoid __sysrq_put_key_op(u8 key, conststruct sysrq_key_op *op_p)
{ int i = sysrq_key_table_key2index(key);
if (i != -1)
sysrq_key_table[i] = op_p;
}
void __handle_sysrq(u8 key, bool check_mask)
{ conststruct sysrq_key_op *op_p; int orig_suppress_printk; int i;
rcu_sysrq_start();
rcu_read_lock(); /* * Enter in the force_console context so that sysrq header is shown to * provide the user with positive feedback. We do not simply emit this * at KERN_EMERG as that would change message routing in the consumers * of /proc/kmsg.
*/
printk_force_console_enter();
op_p = __sysrq_get_key_op(key); if (op_p) { /* * Should we check for enabled operations (/proc/sysrq-trigger * should not) and is the invoked operation enabled?
*/ if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
pr_info("%s\n", op_p->action_msg);
printk_force_console_exit();
op_p->handler(key);
} else {
pr_info("This sysrq operation is disabled.\n");
printk_force_console_exit();
}
} else {
pr_info("HELP : "); /* Only print the help msg once per handler */ for (i = 0; i < ARRAY_SIZE(sysrq_key_table); i++) { if (sysrq_key_table[i]) { int j;
staticvoid sysrq_handle_reset_request(struct sysrq_state *state)
{ if (state->reset_requested)
__handle_sysrq(sysrq_xlate[KEY_B], false);
if (sysrq_reset_downtime_ms)
mod_timer(&state->keyreset_timer,
jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms)); else
sysrq_do_reset(&state->keyreset_timer);
}
staticvoid sysrq_detect_reset_sequence(struct sysrq_state *state, unsignedint code, int value)
{ if (!test_bit(code, state->reset_keybit)) { /* * Pressing any key _not_ in reset sequence cancels * the reset sequence. Also cancelling the timer in * case additional keys were pressed after a reset * has been requested.
*/ if (value && state->reset_seq_cnt) {
state->reset_canceled = true;
timer_delete(&state->keyreset_timer);
}
} elseif (value == 0) { /* * Key release - all keys in the reset sequence need * to be pressed and held for the reset timeout * to hold.
*/
timer_delete(&state->keyreset_timer);
if (--state->reset_seq_cnt == 0)
state->reset_canceled = false;
} elseif (value == 1) { /* key press, not autorepeat */ if (++state->reset_seq_cnt == state->reset_seq_len &&
!state->reset_canceled) {
sysrq_handle_reset_request(state);
}
}
}
case KEY_LEFTALT: case KEY_RIGHTALT: if (!value) { /* One of ALTs is being released */ if (sysrq->active && code == sysrq->alt_use)
sysrq->active = false;
case KEY_LEFTSHIFT: case KEY_RIGHTSHIFT: if (!value)
sysrq->shift = KEY_RESERVED; elseif (value != 2)
sysrq->shift = code; if (sysrq->active)
sysrq->shift_use = sysrq->shift; break;
case KEY_SYSRQ: if (value == 1 && sysrq->alt != KEY_RESERVED) {
sysrq->active = true;
sysrq->alt_use = sysrq->alt; /* either RESERVED (for released) or actual code */
sysrq->shift_use = sysrq->shift; /* * If nothing else will be pressed we'll need * to re-inject Alt-SysRq keysroke.
*/
sysrq->need_reinject = true;
}
/* * Pretend that sysrq was never pressed at all. This * is needed to properly handle KGDB which will try * to release all keys after exiting debugger. If we * do not clear key bit it KGDB will end up sending * release events for Alt and SysRq, potentially * triggering print screen function.
*/ if (sysrq->active)
clear_bit(KEY_SYSRQ, sysrq->handle.dev->key);
break;
default: if (sysrq->active && value && value != 2) { unsignedchar c = sysrq_xlate[code];
sysrq->need_reinject = false; if (sysrq->shift_use != KEY_RESERVED)
c = toupper(c);
__handle_sysrq(c, true);
} break;
}
suppress = sysrq->active;
if (!sysrq->active) {
/* * See if reset sequence has changed since the last time.
*/ if (sysrq->reset_seq_version != sysrq_reset_seq_version)
sysrq_parse_reset_sequence(sysrq);
/* * If we are not suppressing key presses keep track of * keyboard state so we can release keys that have been * pressed before entering SysRq mode.
*/ if (value)
set_bit(code, sysrq->key_down); else
clear_bit(code, sysrq->key_down);
if (was_active)
schedule_work(&sysrq->reinject_work);
/* Check for reset sequence */
sysrq_detect_reset_sequence(sysrq, code, value);
} elseif (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { /* * Pass on release events for keys that was pressed before * entering SysRq mode.
*/
suppress = false;
}
/* * We are matching on KEY_LEFTALT instead of KEY_SYSRQ because not all * keyboards have SysRq key predefined and so user may add it to keymap * later, but we expect all such keyboards to have left alt.
*/ staticconststruct input_device_id sysrq_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { [BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY) },
.keybit = { [BIT_WORD(KEY_LEFTALT)] = BIT_MASK(KEY_LEFTALT) },
},
{ },
};
/* * not really modular, but the easiest way to keep compat with existing * bootargs behaviour is to continue using module_param here.
*/
module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,
&sysrq_reset_seq_len, 0644);
/* * A concurrent __handle_sysrq either got the old op or the new op. * Wait for it to go away before returning, so the code for an old * op is not freed (eg. on module unload) while it is in use.
*/
synchronize_rcu();
#ifdef CONFIG_PROC_FS /* * writing 'C' to /proc/sysrq-trigger is like sysrq-C * Normally, only the first character written is processed. * However, if the first character is an underscore, * all characters are processed.
*/ static ssize_t write_sysrq_trigger(struct file *file, constchar __user *buf,
size_t count, loff_t *ppos)
{ bool bulk = false;
size_t i;
for (i = 0; i < count; i++) { char c;
if (get_user(c, buf + i)) return -EFAULT;
if (c == '_')
bulk = true; else
__handle_sysrq(c, false);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.