diff options
Diffstat (limited to 'kernel/debug/debug_core.c')
-rw-r--r-- | kernel/debug/debug_core.c | 87 |
1 files changed, 57 insertions, 30 deletions
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 0d7c08784efb..0557f24c6bca 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/delay.h> | 41 | #include <linux/delay.h> |
42 | #include <linux/sched.h> | 42 | #include <linux/sched.h> |
43 | #include <linux/sysrq.h> | 43 | #include <linux/sysrq.h> |
44 | #include <linux/reboot.h> | ||
44 | #include <linux/init.h> | 45 | #include <linux/init.h> |
45 | #include <linux/kgdb.h> | 46 | #include <linux/kgdb.h> |
46 | #include <linux/kdb.h> | 47 | #include <linux/kdb.h> |
@@ -52,7 +53,6 @@ | |||
52 | #include <asm/cacheflush.h> | 53 | #include <asm/cacheflush.h> |
53 | #include <asm/byteorder.h> | 54 | #include <asm/byteorder.h> |
54 | #include <linux/atomic.h> | 55 | #include <linux/atomic.h> |
55 | #include <asm/system.h> | ||
56 | 56 | ||
57 | #include "debug_core.h" | 57 | #include "debug_core.h" |
58 | 58 | ||
@@ -75,6 +75,8 @@ static int exception_level; | |||
75 | struct kgdb_io *dbg_io_ops; | 75 | struct kgdb_io *dbg_io_ops; |
76 | static DEFINE_SPINLOCK(kgdb_registration_lock); | 76 | static DEFINE_SPINLOCK(kgdb_registration_lock); |
77 | 77 | ||
78 | /* Action for the reboot notifiter, a global allow kdb to change it */ | ||
79 | static int kgdbreboot; | ||
78 | /* kgdb console driver is loaded */ | 80 | /* kgdb console driver is loaded */ |
79 | static int kgdb_con_registered; | 81 | static int kgdb_con_registered; |
80 | /* determine if kgdb console output should be used */ | 82 | /* determine if kgdb console output should be used */ |
@@ -96,6 +98,7 @@ static int __init opt_kgdb_con(char *str) | |||
96 | early_param("kgdbcon", opt_kgdb_con); | 98 | early_param("kgdbcon", opt_kgdb_con); |
97 | 99 | ||
98 | module_param(kgdb_use_con, int, 0644); | 100 | module_param(kgdb_use_con, int, 0644); |
101 | module_param(kgdbreboot, int, 0644); | ||
99 | 102 | ||
100 | /* | 103 | /* |
101 | * Holds information about breakpoints in a kernel. These breakpoints are | 104 | * Holds information about breakpoints in a kernel. These breakpoints are |
@@ -157,37 +160,39 @@ early_param("nokgdbroundup", opt_nokgdbroundup); | |||
157 | * Weak aliases for breakpoint management, | 160 | * Weak aliases for breakpoint management, |
158 | * can be overriden by architectures when needed: | 161 | * can be overriden by architectures when needed: |
159 | */ | 162 | */ |
160 | int __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) | 163 | int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) |
161 | { | 164 | { |
162 | int err; | 165 | int err; |
163 | 166 | ||
164 | err = probe_kernel_read(saved_instr, (char *)addr, BREAK_INSTR_SIZE); | 167 | err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr, |
168 | BREAK_INSTR_SIZE); | ||
165 | if (err) | 169 | if (err) |
166 | return err; | 170 | return err; |
167 | 171 | err = probe_kernel_write((char *)bpt->bpt_addr, | |
168 | return probe_kernel_write((char *)addr, arch_kgdb_ops.gdb_bpt_instr, | 172 | arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE); |
169 | BREAK_INSTR_SIZE); | 173 | return err; |
170 | } | 174 | } |
171 | 175 | ||
172 | int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) | 176 | int __weak kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) |
173 | { | 177 | { |
174 | return probe_kernel_write((char *)addr, | 178 | return probe_kernel_write((char *)bpt->bpt_addr, |
175 | (char *)bundle, BREAK_INSTR_SIZE); | 179 | (char *)bpt->saved_instr, BREAK_INSTR_SIZE); |
176 | } | 180 | } |
177 | 181 | ||
178 | int __weak kgdb_validate_break_address(unsigned long addr) | 182 | int __weak kgdb_validate_break_address(unsigned long addr) |
179 | { | 183 | { |
180 | char tmp_variable[BREAK_INSTR_SIZE]; | 184 | struct kgdb_bkpt tmp; |
181 | int err; | 185 | int err; |
182 | /* Validate setting the breakpoint and then removing it. In the | 186 | /* Validate setting the breakpoint and then removing it. If the |
183 | * remove fails, the kernel needs to emit a bad message because we | 187 | * remove fails, the kernel needs to emit a bad message because we |
184 | * are deep trouble not being able to put things back the way we | 188 | * are deep trouble not being able to put things back the way we |
185 | * found them. | 189 | * found them. |
186 | */ | 190 | */ |
187 | err = kgdb_arch_set_breakpoint(addr, tmp_variable); | 191 | tmp.bpt_addr = addr; |
192 | err = kgdb_arch_set_breakpoint(&tmp); | ||
188 | if (err) | 193 | if (err) |
189 | return err; | 194 | return err; |
190 | err = kgdb_arch_remove_breakpoint(addr, tmp_variable); | 195 | err = kgdb_arch_remove_breakpoint(&tmp); |
191 | if (err) | 196 | if (err) |
192 | printk(KERN_ERR "KGDB: Critical breakpoint error, kernel " | 197 | printk(KERN_ERR "KGDB: Critical breakpoint error, kernel " |
193 | "memory destroyed at: %lx", addr); | 198 | "memory destroyed at: %lx", addr); |
@@ -231,7 +236,6 @@ static void kgdb_flush_swbreak_addr(unsigned long addr) | |||
231 | */ | 236 | */ |
232 | int dbg_activate_sw_breakpoints(void) | 237 | int dbg_activate_sw_breakpoints(void) |
233 | { | 238 | { |
234 | unsigned long addr; | ||
235 | int error; | 239 | int error; |
236 | int ret = 0; | 240 | int ret = 0; |
237 | int i; | 241 | int i; |
@@ -240,16 +244,15 @@ int dbg_activate_sw_breakpoints(void) | |||
240 | if (kgdb_break[i].state != BP_SET) | 244 | if (kgdb_break[i].state != BP_SET) |
241 | continue; | 245 | continue; |
242 | 246 | ||
243 | addr = kgdb_break[i].bpt_addr; | 247 | error = kgdb_arch_set_breakpoint(&kgdb_break[i]); |
244 | error = kgdb_arch_set_breakpoint(addr, | ||
245 | kgdb_break[i].saved_instr); | ||
246 | if (error) { | 248 | if (error) { |
247 | ret = error; | 249 | ret = error; |
248 | printk(KERN_INFO "KGDB: BP install failed: %lx", addr); | 250 | printk(KERN_INFO "KGDB: BP install failed: %lx", |
251 | kgdb_break[i].bpt_addr); | ||
249 | continue; | 252 | continue; |
250 | } | 253 | } |
251 | 254 | ||
252 | kgdb_flush_swbreak_addr(addr); | 255 | kgdb_flush_swbreak_addr(kgdb_break[i].bpt_addr); |
253 | kgdb_break[i].state = BP_ACTIVE; | 256 | kgdb_break[i].state = BP_ACTIVE; |
254 | } | 257 | } |
255 | return ret; | 258 | return ret; |
@@ -298,7 +301,6 @@ int dbg_set_sw_break(unsigned long addr) | |||
298 | 301 | ||
299 | int dbg_deactivate_sw_breakpoints(void) | 302 | int dbg_deactivate_sw_breakpoints(void) |
300 | { | 303 | { |
301 | unsigned long addr; | ||
302 | int error; | 304 | int error; |
303 | int ret = 0; | 305 | int ret = 0; |
304 | int i; | 306 | int i; |
@@ -306,15 +308,14 @@ int dbg_deactivate_sw_breakpoints(void) | |||
306 | for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { | 308 | for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { |
307 | if (kgdb_break[i].state != BP_ACTIVE) | 309 | if (kgdb_break[i].state != BP_ACTIVE) |
308 | continue; | 310 | continue; |
309 | addr = kgdb_break[i].bpt_addr; | 311 | error = kgdb_arch_remove_breakpoint(&kgdb_break[i]); |
310 | error = kgdb_arch_remove_breakpoint(addr, | ||
311 | kgdb_break[i].saved_instr); | ||
312 | if (error) { | 312 | if (error) { |
313 | printk(KERN_INFO "KGDB: BP remove failed: %lx\n", addr); | 313 | printk(KERN_INFO "KGDB: BP remove failed: %lx\n", |
314 | kgdb_break[i].bpt_addr); | ||
314 | ret = error; | 315 | ret = error; |
315 | } | 316 | } |
316 | 317 | ||
317 | kgdb_flush_swbreak_addr(addr); | 318 | kgdb_flush_swbreak_addr(kgdb_break[i].bpt_addr); |
318 | kgdb_break[i].state = BP_SET; | 319 | kgdb_break[i].state = BP_SET; |
319 | } | 320 | } |
320 | return ret; | 321 | return ret; |
@@ -348,7 +349,6 @@ int kgdb_isremovedbreak(unsigned long addr) | |||
348 | 349 | ||
349 | int dbg_remove_all_break(void) | 350 | int dbg_remove_all_break(void) |
350 | { | 351 | { |
351 | unsigned long addr; | ||
352 | int error; | 352 | int error; |
353 | int i; | 353 | int i; |
354 | 354 | ||
@@ -356,12 +356,10 @@ int dbg_remove_all_break(void) | |||
356 | for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { | 356 | for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { |
357 | if (kgdb_break[i].state != BP_ACTIVE) | 357 | if (kgdb_break[i].state != BP_ACTIVE) |
358 | goto setundefined; | 358 | goto setundefined; |
359 | addr = kgdb_break[i].bpt_addr; | 359 | error = kgdb_arch_remove_breakpoint(&kgdb_break[i]); |
360 | error = kgdb_arch_remove_breakpoint(addr, | ||
361 | kgdb_break[i].saved_instr); | ||
362 | if (error) | 360 | if (error) |
363 | printk(KERN_ERR "KGDB: breakpoint remove failed: %lx\n", | 361 | printk(KERN_ERR "KGDB: breakpoint remove failed: %lx\n", |
364 | addr); | 362 | kgdb_break[i].bpt_addr); |
365 | setundefined: | 363 | setundefined: |
366 | kgdb_break[i].state = BP_UNDEFINED; | 364 | kgdb_break[i].state = BP_UNDEFINED; |
367 | } | 365 | } |
@@ -784,6 +782,33 @@ void __init dbg_late_init(void) | |||
784 | kdb_init(KDB_INIT_FULL); | 782 | kdb_init(KDB_INIT_FULL); |
785 | } | 783 | } |
786 | 784 | ||
785 | static int | ||
786 | dbg_notify_reboot(struct notifier_block *this, unsigned long code, void *x) | ||
787 | { | ||
788 | /* | ||
789 | * Take the following action on reboot notify depending on value: | ||
790 | * 1 == Enter debugger | ||
791 | * 0 == [the default] detatch debug client | ||
792 | * -1 == Do nothing... and use this until the board resets | ||
793 | */ | ||
794 | switch (kgdbreboot) { | ||
795 | case 1: | ||
796 | kgdb_breakpoint(); | ||
797 | case -1: | ||
798 | goto done; | ||
799 | } | ||
800 | if (!dbg_kdb_mode) | ||
801 | gdbstub_exit(code); | ||
802 | done: | ||
803 | return NOTIFY_DONE; | ||
804 | } | ||
805 | |||
806 | static struct notifier_block dbg_reboot_notifier = { | ||
807 | .notifier_call = dbg_notify_reboot, | ||
808 | .next = NULL, | ||
809 | .priority = INT_MAX, | ||
810 | }; | ||
811 | |||
787 | static void kgdb_register_callbacks(void) | 812 | static void kgdb_register_callbacks(void) |
788 | { | 813 | { |
789 | if (!kgdb_io_module_registered) { | 814 | if (!kgdb_io_module_registered) { |
@@ -791,6 +816,7 @@ static void kgdb_register_callbacks(void) | |||
791 | kgdb_arch_init(); | 816 | kgdb_arch_init(); |
792 | if (!dbg_is_early) | 817 | if (!dbg_is_early) |
793 | kgdb_arch_late(); | 818 | kgdb_arch_late(); |
819 | register_reboot_notifier(&dbg_reboot_notifier); | ||
794 | atomic_notifier_chain_register(&panic_notifier_list, | 820 | atomic_notifier_chain_register(&panic_notifier_list, |
795 | &kgdb_panic_event_nb); | 821 | &kgdb_panic_event_nb); |
796 | #ifdef CONFIG_MAGIC_SYSRQ | 822 | #ifdef CONFIG_MAGIC_SYSRQ |
@@ -812,6 +838,7 @@ static void kgdb_unregister_callbacks(void) | |||
812 | */ | 838 | */ |
813 | if (kgdb_io_module_registered) { | 839 | if (kgdb_io_module_registered) { |
814 | kgdb_io_module_registered = 0; | 840 | kgdb_io_module_registered = 0; |
841 | unregister_reboot_notifier(&dbg_reboot_notifier); | ||
815 | atomic_notifier_chain_unregister(&panic_notifier_list, | 842 | atomic_notifier_chain_unregister(&panic_notifier_list, |
816 | &kgdb_panic_event_nb); | 843 | &kgdb_panic_event_nb); |
817 | kgdb_arch_exit(); | 844 | kgdb_arch_exit(); |