Commit a20ae85a authored by Linus Torvalds's avatar Linus Torvalds

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
parents f0a5ec0e 1ba0c172
...@@ -361,6 +361,23 @@ ...@@ -361,6 +361,23 @@
<para>It is possible to use this option with kgdboc on a tty that is not a system console. <para>It is possible to use this option with kgdboc on a tty that is not a system console.
</para> </para>
</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> </sect1>
</chapter> </chapter>
<chapter id="usingKDB"> <chapter id="usingKDB">
......
...@@ -64,11 +64,15 @@ enum regnames { ...@@ -64,11 +64,15 @@ enum regnames {
GDB_PS, /* 17 */ GDB_PS, /* 17 */
GDB_CS, /* 18 */ GDB_CS, /* 18 */
GDB_SS, /* 19 */ GDB_SS, /* 19 */
GDB_DS, /* 20 */
GDB_ES, /* 21 */
GDB_FS, /* 22 */
GDB_GS, /* 23 */
}; };
#define GDB_ORIG_AX 57 #define GDB_ORIG_AX 57
#define DBG_MAX_REG_NUM 20 #define DBG_MAX_REG_NUM 24
/* 17 64 bit regs and 3 32 bit regs */ /* 17 64 bit regs and 5 32 bit regs */
#define NUMREGBYTES ((17 * 8) + (3 * 4)) #define NUMREGBYTES ((17 * 8) + (5 * 4))
#endif /* ! CONFIG_X86_32 */ #endif /* ! CONFIG_X86_32 */
static inline void arch_kgdb_breakpoint(void) static inline void arch_kgdb_breakpoint(void)
......
...@@ -67,8 +67,6 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = ...@@ -67,8 +67,6 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
{ "ss", 4, offsetof(struct pt_regs, ss) }, { "ss", 4, offsetof(struct pt_regs, ss) },
{ "ds", 4, offsetof(struct pt_regs, ds) }, { "ds", 4, offsetof(struct pt_regs, ds) },
{ "es", 4, offsetof(struct pt_regs, es) }, { "es", 4, offsetof(struct pt_regs, es) },
{ "fs", 4, -1 },
{ "gs", 4, -1 },
#else #else
{ "ax", 8, offsetof(struct pt_regs, ax) }, { "ax", 8, offsetof(struct pt_regs, ax) },
{ "bx", 8, offsetof(struct pt_regs, bx) }, { "bx", 8, offsetof(struct pt_regs, bx) },
...@@ -90,7 +88,11 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = ...@@ -90,7 +88,11 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
{ "flags", 4, offsetof(struct pt_regs, flags) }, { "flags", 4, offsetof(struct pt_regs, flags) },
{ "cs", 4, offsetof(struct pt_regs, cs) }, { "cs", 4, offsetof(struct pt_regs, cs) },
{ "ss", 4, offsetof(struct pt_regs, ss) }, { "ss", 4, offsetof(struct pt_regs, ss) },
{ "ds", 4, -1 },
{ "es", 4, -1 },
#endif #endif
{ "fs", 4, -1 },
{ "gs", 4, -1 },
}; };
int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sysrq.h> #include <linux/sysrq.h>
#include <linux/reboot.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kgdb.h> #include <linux/kgdb.h>
#include <linux/kdb.h> #include <linux/kdb.h>
...@@ -75,6 +76,8 @@ static int exception_level; ...@@ -75,6 +76,8 @@ static int exception_level;
struct kgdb_io *dbg_io_ops; struct kgdb_io *dbg_io_ops;
static DEFINE_SPINLOCK(kgdb_registration_lock); 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 */ /* kgdb console driver is loaded */
static int kgdb_con_registered; static int kgdb_con_registered;
/* determine if kgdb console output should be used */ /* determine if kgdb console output should be used */
...@@ -96,6 +99,7 @@ static int __init opt_kgdb_con(char *str) ...@@ -96,6 +99,7 @@ static int __init opt_kgdb_con(char *str)
early_param("kgdbcon", opt_kgdb_con); early_param("kgdbcon", opt_kgdb_con);
module_param(kgdb_use_con, int, 0644); module_param(kgdb_use_con, int, 0644);
module_param(kgdbreboot, int, 0644);
/* /*
* Holds information about breakpoints in a kernel. These breakpoints are * Holds information about breakpoints in a kernel. These breakpoints are
...@@ -784,6 +788,33 @@ void __init dbg_late_init(void) ...@@ -784,6 +788,33 @@ void __init dbg_late_init(void)
kdb_init(KDB_INIT_FULL); 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) static void kgdb_register_callbacks(void)
{ {
if (!kgdb_io_module_registered) { if (!kgdb_io_module_registered) {
...@@ -791,6 +822,7 @@ static void kgdb_register_callbacks(void) ...@@ -791,6 +822,7 @@ static void kgdb_register_callbacks(void)
kgdb_arch_init(); kgdb_arch_init();
if (!dbg_is_early) if (!dbg_is_early)
kgdb_arch_late(); kgdb_arch_late();
register_reboot_notifier(&dbg_reboot_notifier);
atomic_notifier_chain_register(&panic_notifier_list, atomic_notifier_chain_register(&panic_notifier_list,
&kgdb_panic_event_nb); &kgdb_panic_event_nb);
#ifdef CONFIG_MAGIC_SYSRQ #ifdef CONFIG_MAGIC_SYSRQ
...@@ -812,6 +844,7 @@ static void kgdb_unregister_callbacks(void) ...@@ -812,6 +844,7 @@ static void kgdb_unregister_callbacks(void)
*/ */
if (kgdb_io_module_registered) { if (kgdb_io_module_registered) {
kgdb_io_module_registered = 0; kgdb_io_module_registered = 0;
unregister_reboot_notifier(&dbg_reboot_notifier);
atomic_notifier_chain_unregister(&panic_notifier_list, atomic_notifier_chain_unregister(&panic_notifier_list,
&kgdb_panic_event_nb); &kgdb_panic_event_nb);
kgdb_arch_exit(); kgdb_arch_exit();
......
...@@ -1111,6 +1111,13 @@ void gdbstub_exit(int status) ...@@ -1111,6 +1111,13 @@ void gdbstub_exit(int status)
unsigned char checksum, ch, buffer[3]; unsigned char checksum, ch, buffer[3];
int loop; int loop;
if (!kgdb_connected)
return;
kgdb_connected = 0;
if (!dbg_io_ops || dbg_kdb_mode)
return;
buffer[0] = 'W'; buffer[0] = 'W';
buffer[1] = hex_asc_hi(status); buffer[1] = hex_asc_hi(status);
buffer[2] = hex_asc_lo(status); buffer[2] = hex_asc_lo(status);
...@@ -1129,5 +1136,6 @@ void gdbstub_exit(int status) ...@@ -1129,5 +1136,6 @@ void gdbstub_exit(int status)
dbg_io_ops->write_char(hex_asc_lo(checksum)); dbg_io_ops->write_char(hex_asc_lo(checksum));
/* make sure the output is flushed, lest the bootloader clobber it */ /* make sure the output is flushed, lest the bootloader clobber it */
if (dbg_io_ops->flush)
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) ...@@ -153,6 +153,13 @@ static int _kdb_bp_install(struct pt_regs *regs, kdb_bp_t *bp)
} else { } else {
kdb_printf("%s: failed to set breakpoint at 0x%lx\n", kdb_printf("%s: failed to set breakpoint at 0x%lx\n",
__func__, bp->bp_addr); __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 1;
} }
return 0; return 0;
......
...@@ -689,7 +689,7 @@ int vkdb_printf(const char *fmt, va_list ap) ...@@ -689,7 +689,7 @@ int vkdb_printf(const char *fmt, va_list ap)
if (!dbg_kdb_mode && kgdb_connected) { if (!dbg_kdb_mode && kgdb_connected) {
gdbstub_msg_write(kdb_buffer, retlen); gdbstub_msg_write(kdb_buffer, retlen);
} else { } else {
if (!dbg_io_ops->is_console) { if (dbg_io_ops && !dbg_io_ops->is_console) {
len = strlen(kdb_buffer); len = strlen(kdb_buffer);
cp = kdb_buffer; cp = kdb_buffer;
while (len--) { while (len--) {
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ #define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
static int kbd_exists; static int kbd_exists;
static int kbd_last_ret;
/* /*
* Check if the keyboard controller has a keypress for us. * Check if the keyboard controller has a keypress for us.
...@@ -90,8 +91,11 @@ int kdb_get_kbd_char(void) ...@@ -90,8 +91,11 @@ int kdb_get_kbd_char(void)
return -1; return -1;
} }
if ((scancode & 0x80) != 0) if ((scancode & 0x80) != 0) {
if (scancode == 0x9c)
kbd_last_ret = 0;
return -1; return -1;
}
scancode &= 0x7f; scancode &= 0x7f;
...@@ -178,35 +182,82 @@ int kdb_get_kbd_char(void) ...@@ -178,35 +182,82 @@ int kdb_get_kbd_char(void)
return -1; /* ignore unprintables */ return -1; /* ignore unprintables */
} }
if ((scancode & 0x7f) == 0x1c) { 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;
/* /*
* enter key. All done. Absorb the release scancode. * 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) while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0)
; cpu_relax();
/* /*
* Fetch the scancode * Fetch the scancode.
*/ */
scancode = inb(KBD_DATA_REG); scancode = inb(KBD_DATA_REG);
scanstatus = inb(KBD_STATUS_REG); scanstatus = inb(KBD_STATUS_REG);
while (scanstatus & KBD_STAT_MOUSE_OBF) { /*
scancode = inb(KBD_DATA_REG); * Skip mouse input.
scanstatus = inb(KBD_STATUS_REG); */
} if (scanstatus & KBD_STAT_MOUSE_OBF)
continue;
if (scancode != 0x9c) {
/* /*
* Wasn't an enter-release, why not? * 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.
*/ */
kdb_printf("kdb: expected enter got 0x%x status 0x%x\n", if (scancode != 0x9c)
scancode, scanstatus); continue;
}
return 13; return;
} }
return keychar & 0xff;
} }
EXPORT_SYMBOL_GPL(kdb_get_kbd_char);
...@@ -1400,6 +1400,9 @@ int kdb_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error, ...@@ -1400,6 +1400,9 @@ int kdb_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error,
if (KDB_STATE(DOING_SS)) if (KDB_STATE(DOING_SS))
KDB_STATE_CLEAR(SSBPT); KDB_STATE_CLEAR(SSBPT);
/* Clean up any keyboard devices before leaving */
kdb_kbd_cleanup_state();
return result; return result;
} }
......
...@@ -246,6 +246,13 @@ extern void debug_kusage(void); ...@@ -246,6 +246,13 @@ extern void debug_kusage(void);
extern void kdb_set_current_task(struct task_struct *); extern void kdb_set_current_task(struct task_struct *);
extern struct task_struct *kdb_current_task; 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 #ifdef CONFIG_MODULES
extern struct list_head *kdb_modules; extern struct list_head *kdb_modules;
#endif /* CONFIG_MODULES */ #endif /* CONFIG_MODULES */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment