aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Wessel <jason.wessel@windriver.com>2012-03-23 10:35:05 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-04-13 11:14:07 -0400
commite5acefebad4863015271b91f865c62def9bfed82 (patch)
tree7fb6fad7729f00e6cde27d4b249bd2e7627f51af
parenta492297156b336c3fde50ca35e40eb9ddde95478 (diff)
x86,kgdb: Fix DEBUG_RODATA limitation using text_poke()
commit 3751d3e85cf693e10e2c47c03c8caa65e171099b upstream. 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> Inspried-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Signed-off-by: Jason Wessel <jason.wessel@windriver.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--arch/x86/kernel/kgdb.c60
-rw-r--r--drivers/misc/kgdbts.c17
-rw-r--r--include/linux/kgdb.h3
3 files changed, 62 insertions, 18 deletions
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c
index 5f9ecff328b..fc1f48dc998 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>
@@ -710,6 +712,64 @@ void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
710 regs->ip = ip; 712 regs->ip = ip;
711} 713}
712 714
715int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
716{
717 int err;
718 char opc[BREAK_INSTR_SIZE];
719
720 bpt->type = BP_BREAKPOINT;
721 err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr,
722 BREAK_INSTR_SIZE);
723 if (err)
724 return err;
725 err = probe_kernel_write((char *)bpt->bpt_addr,
726 arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
727#ifdef CONFIG_DEBUG_RODATA
728 if (!err)
729 return err;
730 /*
731 * It is safe to call text_poke() because normal kernel execution
732 * is stopped on all cores, so long as the text_mutex is not locked.
733 */
734 if (mutex_is_locked(&text_mutex))
735 return -EBUSY;
736 text_poke((void *)bpt->bpt_addr, arch_kgdb_ops.gdb_bpt_instr,
737 BREAK_INSTR_SIZE);
738 err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE);
739 if (err)
740 return err;
741 if (memcmp(opc, arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE))
742 return -EINVAL;
743 bpt->type = BP_POKE_BREAKPOINT;
744#endif /* CONFIG_DEBUG_RODATA */
745 return err;
746}
747
748int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
749{
750#ifdef CONFIG_DEBUG_RODATA
751 int err;
752 char opc[BREAK_INSTR_SIZE];
753
754 if (bpt->type != BP_POKE_BREAKPOINT)
755 goto knl_write;
756 /*
757 * It is safe to call text_poke() because normal kernel execution
758 * is stopped on all cores, so long as the text_mutex is not locked.
759 */
760 if (mutex_is_locked(&text_mutex))
761 goto knl_write;
762 text_poke((void *)bpt->bpt_addr, bpt->saved_instr, BREAK_INSTR_SIZE);
763 err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE);
764 if (err || memcmp(opc, bpt->saved_instr, BREAK_INSTR_SIZE))
765 goto knl_write;
766 return err;
767knl_write:
768#endif /* CONFIG_DEBUG_RODATA */
769 return probe_kernel_write((char *)bpt->bpt_addr,
770 (char *)bpt->saved_instr, BREAK_INSTR_SIZE);
771}
772
713struct kgdb_arch arch_kgdb_ops = { 773struct kgdb_arch arch_kgdb_ops = {
714 /* Breakpoint instruction: */ 774 /* Breakpoint instruction: */
715 .gdb_bpt_instr = { 0xcc }, 775 .gdb_bpt_instr = { 0xcc },
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 00461380c0f..cdefef231fb 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -967,22 +967,6 @@ static void run_singlestep_break_test(void)
967 kgdbts_break_test(); 967 kgdbts_break_test();
968} 968}
969 969
970static void test_debug_rodata(void)
971{
972#ifdef CONFIG_DEBUG_RODATA
973 /* Until there is an api to write to read-only text segments, use
974 * HW breakpoints for the remainder of any tests, else print a
975 * failure message if hw breakpoints do not work.
976 */
977 if (!(arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT && hwbreaks_ok)) {
978 eprintk("kgdbts: HW breakpoints BROKEN, ending tests\n");
979 return;
980 }
981 force_hwbrks = 1;
982 v1printk("kgdbts:Using HW breakpoints for SW breakpoint tests\n");
983#endif /* CONFIG_DEBUG_RODATA */
984}
985
986static void kgdbts_run_tests(void) 970static void kgdbts_run_tests(void)
987{ 971{
988 char *ptr; 972 char *ptr;
@@ -1015,7 +999,6 @@ static void kgdbts_run_tests(void)
1015 v1printk("kgdbts:RUN access write breakpoint test\n"); 999 v1printk("kgdbts:RUN access write breakpoint test\n");
1016 run_hw_break_test(0); 1000 run_hw_break_test(0);
1017 } 1001 }
1018 test_debug_rodata();
1019 1002
1020 /* required internal KGDB tests */ 1003 /* required internal KGDB tests */
1021 v1printk("kgdbts:RUN plant and detach test\n"); 1004 v1printk("kgdbts:RUN plant and detach test\n");
diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h
index 4d23aa59463..f7192fb4237 100644
--- a/include/linux/kgdb.h
+++ b/include/linux/kgdb.h
@@ -63,7 +63,8 @@ enum kgdb_bptype {
63 BP_HARDWARE_BREAKPOINT, 63 BP_HARDWARE_BREAKPOINT,
64 BP_WRITE_WATCHPOINT, 64 BP_WRITE_WATCHPOINT,
65 BP_READ_WATCHPOINT, 65 BP_READ_WATCHPOINT,
66 BP_ACCESS_WATCHPOINT 66 BP_ACCESS_WATCHPOINT,
67 BP_POKE_BREAKPOINT,
67}; 68};
68 69
69enum kgdb_bpstate { 70enum kgdb_bpstate {