Commit 6c216ec6 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for_linus-3.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/kgdb

Pull KGDB/KDB regression fixes from Jason Wessel:
 - Fix a Smatch warning that appeared in the 3.4 merge window
 - Fix kgdb test suite with SMP for all archs without HW single stepping
 - Fix kgdb sw breakpoints with CONFIG_DEBUG_RODATA=y limitations on x86
 - Fix oops on kgdb test suite with CONFIG_DEBUG_RODATA
 - Fix kgdb test suite with SMP for all archs with HW single stepping

* tag 'for_linus-3.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/kgdb:
  x86,kgdb: Fix DEBUG_RODATA limitation using text_poke()
  kgdb,debug_core: pass the breakpoint struct instead of address and memory
  kgdbts: (2 of 2) fix single step awareness to work correctly with SMP
  kgdbts: (1 of 2) fix single step awareness to work correctly with SMP
  kgdbts: Fix kernel oops with CONFIG_DEBUG_RODATA
  kdb: Fix smatch warning on dbg_io_ops->is_console
parents 58bca4a8 3751d3e8
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <linux/hw_breakpoint.h> #include <linux/hw_breakpoint.h>
#include <linux/uaccess.h>
#include <linux/memory.h>
#include <asm/debugreg.h> #include <asm/debugreg.h>
#include <asm/apicdef.h> #include <asm/apicdef.h>
...@@ -741,6 +743,64 @@ void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) ...@@ -741,6 +743,64 @@ void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
regs->ip = ip; regs->ip = ip;
} }
int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
{
int err;
char opc[BREAK_INSTR_SIZE];
bpt->type = BP_BREAKPOINT;
err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr,
BREAK_INSTR_SIZE);
if (err)
return err;
err = probe_kernel_write((char *)bpt->bpt_addr,
arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
#ifdef CONFIG_DEBUG_RODATA
if (!err)
return err;
/*
* It is safe to call text_poke() because normal kernel execution
* is stopped on all cores, so long as the text_mutex is not locked.
*/
if (mutex_is_locked(&text_mutex))
return -EBUSY;
text_poke((void *)bpt->bpt_addr, arch_kgdb_ops.gdb_bpt_instr,
BREAK_INSTR_SIZE);
err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE);
if (err)
return err;
if (memcmp(opc, arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE))
return -EINVAL;
bpt->type = BP_POKE_BREAKPOINT;
#endif /* CONFIG_DEBUG_RODATA */
return err;
}
int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
{
#ifdef CONFIG_DEBUG_RODATA
int err;
char opc[BREAK_INSTR_SIZE];
if (bpt->type != BP_POKE_BREAKPOINT)
goto knl_write;
/*
* It is safe to call text_poke() because normal kernel execution
* is stopped on all cores, so long as the text_mutex is not locked.
*/
if (mutex_is_locked(&text_mutex))
goto knl_write;
text_poke((void *)bpt->bpt_addr, bpt->saved_instr, BREAK_INSTR_SIZE);
err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE);
if (err || memcmp(opc, bpt->saved_instr, BREAK_INSTR_SIZE))
goto knl_write;
return err;
knl_write:
#endif /* CONFIG_DEBUG_RODATA */
return probe_kernel_write((char *)bpt->bpt_addr,
(char *)bpt->saved_instr, BREAK_INSTR_SIZE);
}
struct kgdb_arch arch_kgdb_ops = { struct kgdb_arch arch_kgdb_ops = {
/* Breakpoint instruction: */ /* Breakpoint instruction: */
.gdb_bpt_instr = { 0xcc }, .gdb_bpt_instr = { 0xcc },
......
This diff is collapsed.
...@@ -63,7 +63,8 @@ enum kgdb_bptype { ...@@ -63,7 +63,8 @@ enum kgdb_bptype {
BP_HARDWARE_BREAKPOINT, BP_HARDWARE_BREAKPOINT,
BP_WRITE_WATCHPOINT, BP_WRITE_WATCHPOINT,
BP_READ_WATCHPOINT, BP_READ_WATCHPOINT,
BP_ACCESS_WATCHPOINT BP_ACCESS_WATCHPOINT,
BP_POKE_BREAKPOINT,
}; };
enum kgdb_bpstate { enum kgdb_bpstate {
...@@ -207,8 +208,8 @@ extern void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc); ...@@ -207,8 +208,8 @@ extern void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc);
/* Optional functions. */ /* Optional functions. */
extern int kgdb_validate_break_address(unsigned long addr); extern int kgdb_validate_break_address(unsigned long addr);
extern int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr); extern int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt);
extern int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle); extern int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt);
/** /**
* kgdb_arch_late - Perform any architecture specific initalization. * kgdb_arch_late - Perform any architecture specific initalization.
......
...@@ -160,37 +160,39 @@ early_param("nokgdbroundup", opt_nokgdbroundup); ...@@ -160,37 +160,39 @@ early_param("nokgdbroundup", opt_nokgdbroundup);
* Weak aliases for breakpoint management, * Weak aliases for breakpoint management,
* can be overriden by architectures when needed: * can be overriden by architectures when needed:
*/ */
int __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
{ {
int err; int err;
err = probe_kernel_read(saved_instr, (char *)addr, BREAK_INSTR_SIZE); err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr,
BREAK_INSTR_SIZE);
if (err) if (err)
return err; return err;
err = probe_kernel_write((char *)bpt->bpt_addr,
return probe_kernel_write((char *)addr, arch_kgdb_ops.gdb_bpt_instr, arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
BREAK_INSTR_SIZE); return err;
} }
int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) int __weak kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
{ {
return probe_kernel_write((char *)addr, return probe_kernel_write((char *)bpt->bpt_addr,
(char *)bundle, BREAK_INSTR_SIZE); (char *)bpt->saved_instr, BREAK_INSTR_SIZE);
} }
int __weak kgdb_validate_break_address(unsigned long addr) int __weak kgdb_validate_break_address(unsigned long addr)
{ {
char tmp_variable[BREAK_INSTR_SIZE]; struct kgdb_bkpt tmp;
int err; int err;
/* Validate setting the breakpoint and then removing it. In the /* Validate setting the breakpoint and then removing it. If the
* remove fails, the kernel needs to emit a bad message because we * remove fails, the kernel needs to emit a bad message because we
* are deep trouble not being able to put things back the way we * are deep trouble not being able to put things back the way we
* found them. * found them.
*/ */
err = kgdb_arch_set_breakpoint(addr, tmp_variable); tmp.bpt_addr = addr;
err = kgdb_arch_set_breakpoint(&tmp);
if (err) if (err)
return err; return err;
err = kgdb_arch_remove_breakpoint(addr, tmp_variable); err = kgdb_arch_remove_breakpoint(&tmp);
if (err) if (err)
printk(KERN_ERR "KGDB: Critical breakpoint error, kernel " printk(KERN_ERR "KGDB: Critical breakpoint error, kernel "
"memory destroyed at: %lx", addr); "memory destroyed at: %lx", addr);
...@@ -234,7 +236,6 @@ static void kgdb_flush_swbreak_addr(unsigned long addr) ...@@ -234,7 +236,6 @@ static void kgdb_flush_swbreak_addr(unsigned long addr)
*/ */
int dbg_activate_sw_breakpoints(void) int dbg_activate_sw_breakpoints(void)
{ {
unsigned long addr;
int error; int error;
int ret = 0; int ret = 0;
int i; int i;
...@@ -243,16 +244,15 @@ int dbg_activate_sw_breakpoints(void) ...@@ -243,16 +244,15 @@ int dbg_activate_sw_breakpoints(void)
if (kgdb_break[i].state != BP_SET) if (kgdb_break[i].state != BP_SET)
continue; continue;
addr = kgdb_break[i].bpt_addr; error = kgdb_arch_set_breakpoint(&kgdb_break[i]);
error = kgdb_arch_set_breakpoint(addr,
kgdb_break[i].saved_instr);
if (error) { if (error) {
ret = error; ret = error;
printk(KERN_INFO "KGDB: BP install failed: %lx", addr); printk(KERN_INFO "KGDB: BP install failed: %lx",
kgdb_break[i].bpt_addr);
continue; continue;
} }
kgdb_flush_swbreak_addr(addr); kgdb_flush_swbreak_addr(kgdb_break[i].bpt_addr);
kgdb_break[i].state = BP_ACTIVE; kgdb_break[i].state = BP_ACTIVE;
} }
return ret; return ret;
...@@ -301,7 +301,6 @@ int dbg_set_sw_break(unsigned long addr) ...@@ -301,7 +301,6 @@ int dbg_set_sw_break(unsigned long addr)
int dbg_deactivate_sw_breakpoints(void) int dbg_deactivate_sw_breakpoints(void)
{ {
unsigned long addr;
int error; int error;
int ret = 0; int ret = 0;
int i; int i;
...@@ -309,15 +308,14 @@ int dbg_deactivate_sw_breakpoints(void) ...@@ -309,15 +308,14 @@ int dbg_deactivate_sw_breakpoints(void)
for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
if (kgdb_break[i].state != BP_ACTIVE) if (kgdb_break[i].state != BP_ACTIVE)
continue; continue;
addr = kgdb_break[i].bpt_addr; error = kgdb_arch_remove_breakpoint(&kgdb_break[i]);
error = kgdb_arch_remove_breakpoint(addr,
kgdb_break[i].saved_instr);
if (error) { if (error) {
printk(KERN_INFO "KGDB: BP remove failed: %lx\n", addr); printk(KERN_INFO "KGDB: BP remove failed: %lx\n",
kgdb_break[i].bpt_addr);
ret = error; ret = error;
} }
kgdb_flush_swbreak_addr(addr); kgdb_flush_swbreak_addr(kgdb_break[i].bpt_addr);
kgdb_break[i].state = BP_SET; kgdb_break[i].state = BP_SET;
} }
return ret; return ret;
...@@ -351,7 +349,6 @@ int kgdb_isremovedbreak(unsigned long addr) ...@@ -351,7 +349,6 @@ int kgdb_isremovedbreak(unsigned long addr)
int dbg_remove_all_break(void) int dbg_remove_all_break(void)
{ {
unsigned long addr;
int error; int error;
int i; int i;
...@@ -359,12 +356,10 @@ int dbg_remove_all_break(void) ...@@ -359,12 +356,10 @@ int dbg_remove_all_break(void)
for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
if (kgdb_break[i].state != BP_ACTIVE) if (kgdb_break[i].state != BP_ACTIVE)
goto setundefined; goto setundefined;
addr = kgdb_break[i].bpt_addr; error = kgdb_arch_remove_breakpoint(&kgdb_break[i]);
error = kgdb_arch_remove_breakpoint(addr,
kgdb_break[i].saved_instr);
if (error) if (error)
printk(KERN_ERR "KGDB: breakpoint remove failed: %lx\n", printk(KERN_ERR "KGDB: breakpoint remove failed: %lx\n",
addr); kgdb_break[i].bpt_addr);
setundefined: setundefined:
kgdb_break[i].state = BP_UNDEFINED; kgdb_break[i].state = BP_UNDEFINED;
} }
......
...@@ -743,7 +743,7 @@ int vkdb_printf(const char *fmt, va_list ap) ...@@ -743,7 +743,7 @@ int vkdb_printf(const char *fmt, va_list ap)
kdb_input_flush(); kdb_input_flush();
c = console_drivers; c = console_drivers;
if (!dbg_io_ops->is_console) { if (dbg_io_ops && !dbg_io_ops->is_console) {
len = strlen(moreprompt); len = strlen(moreprompt);
cp = moreprompt; cp = moreprompt;
while (len--) { while (len--) {
......
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