mirror of
https://github.com/torvalds/linux.git
synced 2025-01-01 15:51:46 +00:00
Fixes:
- Fix KDB keyboard repeat scan codes and leaked keyboard events - Fix kernel crash with kdb_printf() for users who compile new kdb_printf()'s in early code - Return all segment registers to gdb on x86_64 Features: - KDB/KGDB hook the reboot notifier and end user can control if it stops, detaches or does nothing (updated docs as well) - Notify users who use CONFIG_DEBUG_RODATA to use hw breakpoints -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJPbJ17AAoJEIciOldedpOjQeIP/AkUxQFJ7O4aLrLYHl62EHnh spkgkd+nBzIzcKyV73alkrVBaR2WE2822aAPQmAPBP8/X283DZJJjqgDCUNVI1Mf CIZ7g8AQHRnS+bAZmof5Jss4malZn4byLvG/cfpOivrsye+4A8MdrAKKM3pYWNVy 4xABkcEknVEEamdNEhHrcPd+xehretfw7+9mmU8hfjqHb/5cXFB7JwDcf4tF7ozT MDyN4xKtOn1W/ftQl0t6izksCUuPyqKzIfUyAy0j6AwTgsEavXu56S52T1UoB2ZI JcwLn/ZpN4eGCWVodY04R3jzaMtKFb6ImY40wsb5wl3CU3Ecy5syMU6z4fg3cvjH /KE6xWF61j4yiE5lzjeJVtKyxwalthzrr56qU2uEwrsEVmo3SOnW9sm0cwouqa7j /TAMlhZuGgbZGesFwdaUKI5OLGoki+pRQ0Gaq3TsbZwpPC5Bimkq0bIvruruKJCP QWVkEvb5TZgxCFS3AvniePOm7Hc2wD9zXB3OfN3o91pCom0ryDBIthcLlwhVeNCo Jd67pnJJNVULPF/1qVicZihKHxvG3DUb4E9pUcbgJplBke+isi+3eHOvnQrYFjIG iCamE9qvVbsQm/OFV8MOJ5mfPs9R+nb/jNzTO8JDmBc8AL7nRDV3AFGjW68x/KWT ERcqNEGJ4QuVAxfejq76 =SXu9 -----END PGP SIGNATURE----- Merge tag 'for_linus-3.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/kgdb Pull KGDB/KDB updates from Jason Wessel: "Fixes: - Fix KDB keyboard repeat scan codes and leaked keyboard events - Fix kernel crash with kdb_printf() for users who compile new kdb_printf()'s in early code - Return all segment registers to gdb on x86_64 Features: - KDB/KGDB hook the reboot notifier and end user can control if it stops, detaches or does nothing (updated docs as well) - Notify users who use CONFIG_DEBUG_RODATA to use hw breakpoints" * tag 'for_linus-3.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/kgdb: kdb: Add message about CONFIG_DEBUG_RODATA on failure to install breakpoint kdb: Avoid using dbg_io_ops until it is initialized kgdb,debug_core: add the ability to control the reboot notifier KDB: Fix usability issues relating to the 'enter' key. kgdb,debug-core,gdbstub: Hook the reboot notifier for debugger detach kgdb: Respect that flush op is optional kgdb: x86: Return all segment registers also in 64-bit mode
This commit is contained in:
commit
a20ae85aba
@ -361,6 +361,23 @@
|
||||
<para>It is possible to use this option with kgdboc on a tty that is not a system console.
|
||||
</para>
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="kgdbreboot">
|
||||
<title>Run time parameter: kgdbreboot</title>
|
||||
<para> The kgdbreboot feature allows you to change how the debugger
|
||||
deals with the reboot notification. You have 3 choices for the
|
||||
behavior. The default behavior is always set to 0.</para>
|
||||
<orderedlist>
|
||||
<listitem><para>echo -1 > /sys/module/debug_core/parameters/kgdbreboot</para>
|
||||
<para>Ignore the reboot notification entirely.</para>
|
||||
</listitem>
|
||||
<listitem><para>echo 0 > /sys/module/debug_core/parameters/kgdbreboot</para>
|
||||
<para>Send the detach message to any attached debugger client.</para>
|
||||
</listitem>
|
||||
<listitem><para>echo 1 > /sys/module/debug_core/parameters/kgdbreboot</para>
|
||||
<para>Enter the debugger on reboot notify.</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</sect1>
|
||||
</chapter>
|
||||
<chapter id="usingKDB">
|
||||
|
@ -64,11 +64,15 @@ enum regnames {
|
||||
GDB_PS, /* 17 */
|
||||
GDB_CS, /* 18 */
|
||||
GDB_SS, /* 19 */
|
||||
GDB_DS, /* 20 */
|
||||
GDB_ES, /* 21 */
|
||||
GDB_FS, /* 22 */
|
||||
GDB_GS, /* 23 */
|
||||
};
|
||||
#define GDB_ORIG_AX 57
|
||||
#define DBG_MAX_REG_NUM 20
|
||||
/* 17 64 bit regs and 3 32 bit regs */
|
||||
#define NUMREGBYTES ((17 * 8) + (3 * 4))
|
||||
#define DBG_MAX_REG_NUM 24
|
||||
/* 17 64 bit regs and 5 32 bit regs */
|
||||
#define NUMREGBYTES ((17 * 8) + (5 * 4))
|
||||
#endif /* ! CONFIG_X86_32 */
|
||||
|
||||
static inline void arch_kgdb_breakpoint(void)
|
||||
|
@ -67,8 +67,6 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
|
||||
{ "ss", 4, offsetof(struct pt_regs, ss) },
|
||||
{ "ds", 4, offsetof(struct pt_regs, ds) },
|
||||
{ "es", 4, offsetof(struct pt_regs, es) },
|
||||
{ "fs", 4, -1 },
|
||||
{ "gs", 4, -1 },
|
||||
#else
|
||||
{ "ax", 8, offsetof(struct pt_regs, ax) },
|
||||
{ "bx", 8, offsetof(struct pt_regs, bx) },
|
||||
@ -90,7 +88,11 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
|
||||
{ "flags", 4, offsetof(struct pt_regs, flags) },
|
||||
{ "cs", 4, offsetof(struct pt_regs, cs) },
|
||||
{ "ss", 4, offsetof(struct pt_regs, ss) },
|
||||
{ "ds", 4, -1 },
|
||||
{ "es", 4, -1 },
|
||||
#endif
|
||||
{ "fs", 4, -1 },
|
||||
{ "gs", 4, -1 },
|
||||
};
|
||||
|
||||
int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/kdb.h>
|
||||
@ -75,6 +76,8 @@ static int exception_level;
|
||||
struct kgdb_io *dbg_io_ops;
|
||||
static DEFINE_SPINLOCK(kgdb_registration_lock);
|
||||
|
||||
/* Action for the reboot notifiter, a global allow kdb to change it */
|
||||
static int kgdbreboot;
|
||||
/* kgdb console driver is loaded */
|
||||
static int kgdb_con_registered;
|
||||
/* determine if kgdb console output should be used */
|
||||
@ -96,6 +99,7 @@ static int __init opt_kgdb_con(char *str)
|
||||
early_param("kgdbcon", opt_kgdb_con);
|
||||
|
||||
module_param(kgdb_use_con, int, 0644);
|
||||
module_param(kgdbreboot, int, 0644);
|
||||
|
||||
/*
|
||||
* Holds information about breakpoints in a kernel. These breakpoints are
|
||||
@ -784,6 +788,33 @@ void __init dbg_late_init(void)
|
||||
kdb_init(KDB_INIT_FULL);
|
||||
}
|
||||
|
||||
static int
|
||||
dbg_notify_reboot(struct notifier_block *this, unsigned long code, void *x)
|
||||
{
|
||||
/*
|
||||
* Take the following action on reboot notify depending on value:
|
||||
* 1 == Enter debugger
|
||||
* 0 == [the default] detatch debug client
|
||||
* -1 == Do nothing... and use this until the board resets
|
||||
*/
|
||||
switch (kgdbreboot) {
|
||||
case 1:
|
||||
kgdb_breakpoint();
|
||||
case -1:
|
||||
goto done;
|
||||
}
|
||||
if (!dbg_kdb_mode)
|
||||
gdbstub_exit(code);
|
||||
done:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block dbg_reboot_notifier = {
|
||||
.notifier_call = dbg_notify_reboot,
|
||||
.next = NULL,
|
||||
.priority = INT_MAX,
|
||||
};
|
||||
|
||||
static void kgdb_register_callbacks(void)
|
||||
{
|
||||
if (!kgdb_io_module_registered) {
|
||||
@ -791,6 +822,7 @@ static void kgdb_register_callbacks(void)
|
||||
kgdb_arch_init();
|
||||
if (!dbg_is_early)
|
||||
kgdb_arch_late();
|
||||
register_reboot_notifier(&dbg_reboot_notifier);
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&kgdb_panic_event_nb);
|
||||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
@ -812,6 +844,7 @@ static void kgdb_unregister_callbacks(void)
|
||||
*/
|
||||
if (kgdb_io_module_registered) {
|
||||
kgdb_io_module_registered = 0;
|
||||
unregister_reboot_notifier(&dbg_reboot_notifier);
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&kgdb_panic_event_nb);
|
||||
kgdb_arch_exit();
|
||||
|
@ -1111,6 +1111,13 @@ void gdbstub_exit(int status)
|
||||
unsigned char checksum, ch, buffer[3];
|
||||
int loop;
|
||||
|
||||
if (!kgdb_connected)
|
||||
return;
|
||||
kgdb_connected = 0;
|
||||
|
||||
if (!dbg_io_ops || dbg_kdb_mode)
|
||||
return;
|
||||
|
||||
buffer[0] = 'W';
|
||||
buffer[1] = hex_asc_hi(status);
|
||||
buffer[2] = hex_asc_lo(status);
|
||||
@ -1129,5 +1136,6 @@ void gdbstub_exit(int status)
|
||||
dbg_io_ops->write_char(hex_asc_lo(checksum));
|
||||
|
||||
/* make sure the output is flushed, lest the bootloader clobber it */
|
||||
dbg_io_ops->flush();
|
||||
if (dbg_io_ops->flush)
|
||||
dbg_io_ops->flush();
|
||||
}
|
||||
|
@ -153,6 +153,13 @@ static int _kdb_bp_install(struct pt_regs *regs, kdb_bp_t *bp)
|
||||
} else {
|
||||
kdb_printf("%s: failed to set breakpoint at 0x%lx\n",
|
||||
__func__, bp->bp_addr);
|
||||
#ifdef CONFIG_DEBUG_RODATA
|
||||
if (!bp->bp_type) {
|
||||
kdb_printf("Software breakpoints are unavailable.\n"
|
||||
" Change the kernel CONFIG_DEBUG_RODATA=n\n"
|
||||
" OR use hw breaks: help bph\n");
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -689,7 +689,7 @@ kdb_printit:
|
||||
if (!dbg_kdb_mode && kgdb_connected) {
|
||||
gdbstub_msg_write(kdb_buffer, retlen);
|
||||
} else {
|
||||
if (!dbg_io_ops->is_console) {
|
||||
if (dbg_io_ops && !dbg_io_ops->is_console) {
|
||||
len = strlen(kdb_buffer);
|
||||
cp = kdb_buffer;
|
||||
while (len--) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
|
||||
|
||||
static int kbd_exists;
|
||||
static int kbd_last_ret;
|
||||
|
||||
/*
|
||||
* Check if the keyboard controller has a keypress for us.
|
||||
@ -90,8 +91,11 @@ int kdb_get_kbd_char(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((scancode & 0x80) != 0)
|
||||
if ((scancode & 0x80) != 0) {
|
||||
if (scancode == 0x9c)
|
||||
kbd_last_ret = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
scancode &= 0x7f;
|
||||
|
||||
@ -178,35 +182,82 @@ int kdb_get_kbd_char(void)
|
||||
return -1; /* ignore unprintables */
|
||||
}
|
||||
|
||||
if ((scancode & 0x7f) == 0x1c) {
|
||||
/*
|
||||
* enter key. All done. Absorb the release scancode.
|
||||
*/
|
||||
while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0)
|
||||
;
|
||||
|
||||
/*
|
||||
* Fetch the scancode
|
||||
*/
|
||||
scancode = inb(KBD_DATA_REG);
|
||||
scanstatus = inb(KBD_STATUS_REG);
|
||||
|
||||
while (scanstatus & KBD_STAT_MOUSE_OBF) {
|
||||
scancode = inb(KBD_DATA_REG);
|
||||
scanstatus = inb(KBD_STATUS_REG);
|
||||
}
|
||||
|
||||
if (scancode != 0x9c) {
|
||||
/*
|
||||
* Wasn't an enter-release, why not?
|
||||
*/
|
||||
kdb_printf("kdb: expected enter got 0x%x status 0x%x\n",
|
||||
scancode, scanstatus);
|
||||
}
|
||||
|
||||
if (scancode == 0x1c) {
|
||||
kbd_last_ret = 1;
|
||||
return 13;
|
||||
}
|
||||
|
||||
return keychar & 0xff;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kdb_get_kbd_char);
|
||||
|
||||
/*
|
||||
* Best effort cleanup of ENTER break codes on leaving KDB. Called on
|
||||
* exiting KDB, when we know we processed an ENTER or KP ENTER scan
|
||||
* code.
|
||||
*/
|
||||
void kdb_kbd_cleanup_state(void)
|
||||
{
|
||||
int scancode, scanstatus;
|
||||
|
||||
/*
|
||||
* Nothing to clean up, since either
|
||||
* ENTER was never pressed, or has already
|
||||
* gotten cleaned up.
|
||||
*/
|
||||
if (!kbd_last_ret)
|
||||
return;
|
||||
|
||||
kbd_last_ret = 0;
|
||||
/*
|
||||
* Enter key. Need to absorb the break code here, lest it gets
|
||||
* leaked out if we exit KDB as the result of processing 'g'.
|
||||
*
|
||||
* This has several interesting implications:
|
||||
* + Need to handle KP ENTER, which has break code 0xe0 0x9c.
|
||||
* + Need to handle repeat ENTER and repeat KP ENTER. Repeats
|
||||
* only get a break code at the end of the repeated
|
||||
* sequence. This means we can't propagate the repeated key
|
||||
* press, and must swallow it away.
|
||||
* + Need to handle possible PS/2 mouse input.
|
||||
* + Need to handle mashed keys.
|
||||
*/
|
||||
|
||||
while (1) {
|
||||
while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0)
|
||||
cpu_relax();
|
||||
|
||||
/*
|
||||
* Fetch the scancode.
|
||||
*/
|
||||
scancode = inb(KBD_DATA_REG);
|
||||
scanstatus = inb(KBD_STATUS_REG);
|
||||
|
||||
/*
|
||||
* Skip mouse input.
|
||||
*/
|
||||
if (scanstatus & KBD_STAT_MOUSE_OBF)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If we see 0xe0, this is either a break code for KP
|
||||
* ENTER, or a repeat make for KP ENTER. Either way,
|
||||
* since the second byte is equivalent to an ENTER,
|
||||
* skip the 0xe0 and try again.
|
||||
*
|
||||
* If we see 0x1c, this must be a repeat ENTER or KP
|
||||
* ENTER (and we swallowed 0xe0 before). Try again.
|
||||
*
|
||||
* We can also see make and break codes for other keys
|
||||
* mashed before or after pressing ENTER. Thus, if we
|
||||
* see anything other than 0x9c, we have to try again.
|
||||
*
|
||||
* Note, if you held some key as ENTER was depressed,
|
||||
* that break code would get leaked out.
|
||||
*/
|
||||
if (scancode != 0x9c)
|
||||
continue;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1400,6 +1400,9 @@ int kdb_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error,
|
||||
if (KDB_STATE(DOING_SS))
|
||||
KDB_STATE_CLEAR(SSBPT);
|
||||
|
||||
/* Clean up any keyboard devices before leaving */
|
||||
kdb_kbd_cleanup_state();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -246,6 +246,13 @@ extern void debug_kusage(void);
|
||||
|
||||
extern void kdb_set_current_task(struct task_struct *);
|
||||
extern struct task_struct *kdb_current_task;
|
||||
|
||||
#ifdef CONFIG_KDB_KEYBOARD
|
||||
extern void kdb_kbd_cleanup_state(void);
|
||||
#else /* ! CONFIG_KDB_KEYBOARD */
|
||||
#define kdb_kbd_cleanup_state()
|
||||
#endif /* ! CONFIG_KDB_KEYBOARD */
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
extern struct list_head *kdb_modules;
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
Loading…
Reference in New Issue
Block a user