aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorJason Wessel <jason.wessel@windriver.com>2012-03-23 10:35:05 -0400
committerJason Wessel <jason.wessel@windriver.com>2012-03-29 18:41:25 -0400
commit3751d3e85cf693e10e2c47c03c8caa65e171099b (patch)
tree65d123dac17103f6862831bcb4271b263805fd9f /arch/x86
parent98b54aa1a2241b59372468bd1e9c2d207bdba54b (diff)
x86,kgdb: Fix DEBUG_RODATA limitation using text_poke()
There has long been a limitation using software breakpoints with a kernel compiled with CONFIG_DEBUG_RODATA going back to 2.6.26. For this particular patch, it will apply cleanly and has been tested all the way back to 2.6.36. The kprobes code uses the text_poke() function which accommodates writing a breakpoint into a read-only page. The x86 kgdb code can solve the problem similarly by overriding the default breakpoint set/remove routines and using text_poke() directly. The x86 kgdb code will first attempt to use the traditional probe_kernel_write(), and next try using a the text_poke() function. The break point install method is tracked such that the correct break point removal routine will get called later on. Cc: x86@kernel.org Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: stable@vger.kernel.org # >= 2.6.36 Inspried-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/kernel/kgdb.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c
index fdc37b3d0ce3..b9bd9d8de665 100644
--- a/arch/x86/kernel/kgdb.c
+++ b/arch/x86/kernel/kgdb.c
@@ -43,6 +43,8 @@
43#include <linux/smp.h> 43#include <linux/smp.h>
44#include <linux/nmi.h> 44#include <linux/nmi.h>
45#include <linux/hw_breakpoint.h> 45#include <linux/hw_breakpoint.h>
46#include <linux/uaccess.h>
47#include <linux/memory.h>
46 48
47#include <asm/debugreg.h> 49#include <asm/debugreg.h>
48#include <asm/apicdef.h> 50#include <asm/apicdef.h>
@@ -742,6 +744,64 @@ void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
742 regs->ip = ip; 744 regs->ip = ip;
743} 745}
744 746
747int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
748{
749 int err;
750 char opc[BREAK_INSTR_SIZE];
751
752 bpt->type = BP_BREAKPOINT;
753 err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr,
754 BREAK_INSTR_SIZE);
755 if (err)
756 return err;
757 err = probe_kernel_write((char *)bpt->bpt_addr,
758 arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
759#ifdef CONFIG_DEBUG_RODATA
760 if (!err)
761 return err;
762 /*
763 * It is safe to call text_poke() because normal kernel execution
764 * is stopped on all cores, so long as the text_mutex is not locked.
765 */
766 if (mutex_is_locked(&text_mutex))
767 return -EBUSY;
768 text_poke((void *)bpt->bpt_addr, arch_kgdb_ops.gdb_bpt_instr,
769 BREAK_INSTR_SIZE);
770 err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE);
771 if (err)
772 return err;
773 if (memcmp(opc, arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE))
774 return -EINVAL;
775 bpt->type = BP_POKE_BREAKPOINT;
776#endif /* CONFIG_DEBUG_RODATA */
777 return err;
778}
779
780int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
781{
782#ifdef CONFIG_DEBUG_RODATA
783 int err;
784 char opc[BREAK_INSTR_SIZE];
785
786 if (bpt->type != BP_POKE_BREAKPOINT)
787 goto knl_write;
788 /*
789 * It is safe to call text_poke() because normal kernel execution
790 * is stopped on all cores, so long as the text_mutex is not locked.
791 */
792 if (mutex_is_locked(&text_mutex))
793 goto knl_write;
794 text_poke((void *)bpt->bpt_addr, bpt->saved_instr, BREAK_INSTR_SIZE);
795 err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE);
796 if (err || memcmp(opc, bpt->saved_instr, BREAK_INSTR_SIZE))
797 goto knl_write;
798 return err;
799knl_write:
800#endif /* CONFIG_DEBUG_RODATA */
801 return probe_kernel_write((char *)bpt->bpt_addr,
802 (char *)bpt->saved_instr, BREAK_INSTR_SIZE);
803}
804
745struct kgdb_arch arch_kgdb_ops = { 805struct kgdb_arch arch_kgdb_ops = {
746 /* Breakpoint instruction: */ 806 /* Breakpoint instruction: */
747 .gdb_bpt_instr = { 0xcc }, 807 .gdb_bpt_instr = { 0xcc },