diff options
Diffstat (limited to 'arch/x86/kernel/kgdb.c')
-rw-r--r-- | arch/x86/kernel/kgdb.c | 103 |
1 files changed, 64 insertions, 39 deletions
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index b2258ca91003..4f4af75b9482 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c | |||
@@ -47,20 +47,8 @@ | |||
47 | #include <asm/debugreg.h> | 47 | #include <asm/debugreg.h> |
48 | #include <asm/apicdef.h> | 48 | #include <asm/apicdef.h> |
49 | #include <asm/system.h> | 49 | #include <asm/system.h> |
50 | |||
51 | #include <asm/apic.h> | 50 | #include <asm/apic.h> |
52 | 51 | ||
53 | /* | ||
54 | * Put the error code here just in case the user cares: | ||
55 | */ | ||
56 | static int gdb_x86errcode; | ||
57 | |||
58 | /* | ||
59 | * Likewise, the vector number here (since GDB only gets the signal | ||
60 | * number through the usual means, and that's not very specific): | ||
61 | */ | ||
62 | static int gdb_x86vector = -1; | ||
63 | |||
64 | /** | 52 | /** |
65 | * pt_regs_to_gdb_regs - Convert ptrace regs to GDB regs | 53 | * pt_regs_to_gdb_regs - Convert ptrace regs to GDB regs |
66 | * @gdb_regs: A pointer to hold the registers in the order GDB wants. | 54 | * @gdb_regs: A pointer to hold the registers in the order GDB wants. |
@@ -211,6 +199,8 @@ static struct hw_breakpoint { | |||
211 | struct perf_event **pev; | 199 | struct perf_event **pev; |
212 | } breakinfo[4]; | 200 | } breakinfo[4]; |
213 | 201 | ||
202 | static unsigned long early_dr7; | ||
203 | |||
214 | static void kgdb_correct_hw_break(void) | 204 | static void kgdb_correct_hw_break(void) |
215 | { | 205 | { |
216 | int breakno; | 206 | int breakno; |
@@ -222,6 +212,14 @@ static void kgdb_correct_hw_break(void) | |||
222 | int cpu = raw_smp_processor_id(); | 212 | int cpu = raw_smp_processor_id(); |
223 | if (!breakinfo[breakno].enabled) | 213 | if (!breakinfo[breakno].enabled) |
224 | continue; | 214 | continue; |
215 | if (dbg_is_early) { | ||
216 | set_debugreg(breakinfo[breakno].addr, breakno); | ||
217 | early_dr7 |= encode_dr7(breakno, | ||
218 | breakinfo[breakno].len, | ||
219 | breakinfo[breakno].type); | ||
220 | set_debugreg(early_dr7, 7); | ||
221 | continue; | ||
222 | } | ||
225 | bp = *per_cpu_ptr(breakinfo[breakno].pev, cpu); | 223 | bp = *per_cpu_ptr(breakinfo[breakno].pev, cpu); |
226 | info = counter_arch_bp(bp); | 224 | info = counter_arch_bp(bp); |
227 | if (bp->attr.disabled != 1) | 225 | if (bp->attr.disabled != 1) |
@@ -236,7 +234,8 @@ static void kgdb_correct_hw_break(void) | |||
236 | if (!val) | 234 | if (!val) |
237 | bp->attr.disabled = 0; | 235 | bp->attr.disabled = 0; |
238 | } | 236 | } |
239 | hw_breakpoint_restore(); | 237 | if (!dbg_is_early) |
238 | hw_breakpoint_restore(); | ||
240 | } | 239 | } |
241 | 240 | ||
242 | static int hw_break_reserve_slot(int breakno) | 241 | static int hw_break_reserve_slot(int breakno) |
@@ -245,6 +244,9 @@ static int hw_break_reserve_slot(int breakno) | |||
245 | int cnt = 0; | 244 | int cnt = 0; |
246 | struct perf_event **pevent; | 245 | struct perf_event **pevent; |
247 | 246 | ||
247 | if (dbg_is_early) | ||
248 | return 0; | ||
249 | |||
248 | for_each_online_cpu(cpu) { | 250 | for_each_online_cpu(cpu) { |
249 | cnt++; | 251 | cnt++; |
250 | pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); | 252 | pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); |
@@ -270,6 +272,9 @@ static int hw_break_release_slot(int breakno) | |||
270 | struct perf_event **pevent; | 272 | struct perf_event **pevent; |
271 | int cpu; | 273 | int cpu; |
272 | 274 | ||
275 | if (dbg_is_early) | ||
276 | return 0; | ||
277 | |||
273 | for_each_online_cpu(cpu) { | 278 | for_each_online_cpu(cpu) { |
274 | pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); | 279 | pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); |
275 | if (dbg_release_bp_slot(*pevent)) | 280 | if (dbg_release_bp_slot(*pevent)) |
@@ -314,7 +319,11 @@ static void kgdb_remove_all_hw_break(void) | |||
314 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); | 319 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); |
315 | if (bp->attr.disabled == 1) | 320 | if (bp->attr.disabled == 1) |
316 | continue; | 321 | continue; |
317 | arch_uninstall_hw_breakpoint(bp); | 322 | if (dbg_is_early) |
323 | early_dr7 &= ~encode_dr7(i, breakinfo[i].len, | ||
324 | breakinfo[i].type); | ||
325 | else | ||
326 | arch_uninstall_hw_breakpoint(bp); | ||
318 | bp->attr.disabled = 1; | 327 | bp->attr.disabled = 1; |
319 | } | 328 | } |
320 | } | 329 | } |
@@ -391,6 +400,11 @@ void kgdb_disable_hw_debug(struct pt_regs *regs) | |||
391 | for (i = 0; i < 4; i++) { | 400 | for (i = 0; i < 4; i++) { |
392 | if (!breakinfo[i].enabled) | 401 | if (!breakinfo[i].enabled) |
393 | continue; | 402 | continue; |
403 | if (dbg_is_early) { | ||
404 | early_dr7 &= ~encode_dr7(i, breakinfo[i].len, | ||
405 | breakinfo[i].type); | ||
406 | continue; | ||
407 | } | ||
394 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); | 408 | bp = *per_cpu_ptr(breakinfo[i].pev, cpu); |
395 | if (bp->attr.disabled == 1) | 409 | if (bp->attr.disabled == 1) |
396 | continue; | 410 | continue; |
@@ -399,23 +413,6 @@ void kgdb_disable_hw_debug(struct pt_regs *regs) | |||
399 | } | 413 | } |
400 | } | 414 | } |
401 | 415 | ||
402 | /** | ||
403 | * kgdb_post_primary_code - Save error vector/code numbers. | ||
404 | * @regs: Original pt_regs. | ||
405 | * @e_vector: Original error vector. | ||
406 | * @err_code: Original error code. | ||
407 | * | ||
408 | * This is needed on architectures which support SMP and KGDB. | ||
409 | * This function is called after all the slave cpus have been put | ||
410 | * to a know spin state and the primary CPU has control over KGDB. | ||
411 | */ | ||
412 | void kgdb_post_primary_code(struct pt_regs *regs, int e_vector, int err_code) | ||
413 | { | ||
414 | /* primary processor is completely in the debugger */ | ||
415 | gdb_x86vector = e_vector; | ||
416 | gdb_x86errcode = err_code; | ||
417 | } | ||
418 | |||
419 | #ifdef CONFIG_SMP | 416 | #ifdef CONFIG_SMP |
420 | /** | 417 | /** |
421 | * kgdb_roundup_cpus - Get other CPUs into a holding pattern | 418 | * kgdb_roundup_cpus - Get other CPUs into a holding pattern |
@@ -567,7 +564,7 @@ static int __kgdb_notify(struct die_args *args, unsigned long cmd) | |||
567 | return NOTIFY_DONE; | 564 | return NOTIFY_DONE; |
568 | } | 565 | } |
569 | 566 | ||
570 | if (kgdb_handle_exception(args->trapnr, args->signr, args->err, regs)) | 567 | if (kgdb_handle_exception(args->trapnr, args->signr, cmd, regs)) |
571 | return NOTIFY_DONE; | 568 | return NOTIFY_DONE; |
572 | 569 | ||
573 | /* Must touch watchdog before return to normal operation */ | 570 | /* Must touch watchdog before return to normal operation */ |
@@ -575,6 +572,26 @@ static int __kgdb_notify(struct die_args *args, unsigned long cmd) | |||
575 | return NOTIFY_STOP; | 572 | return NOTIFY_STOP; |
576 | } | 573 | } |
577 | 574 | ||
575 | #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP | ||
576 | int kgdb_ll_trap(int cmd, const char *str, | ||
577 | struct pt_regs *regs, long err, int trap, int sig) | ||
578 | { | ||
579 | struct die_args args = { | ||
580 | .regs = regs, | ||
581 | .str = str, | ||
582 | .err = err, | ||
583 | .trapnr = trap, | ||
584 | .signr = sig, | ||
585 | |||
586 | }; | ||
587 | |||
588 | if (!kgdb_io_module_registered) | ||
589 | return NOTIFY_DONE; | ||
590 | |||
591 | return __kgdb_notify(&args, cmd); | ||
592 | } | ||
593 | #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */ | ||
594 | |||
578 | static int | 595 | static int |
579 | kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr) | 596 | kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr) |
580 | { | 597 | { |
@@ -605,14 +622,15 @@ static struct notifier_block kgdb_notifier = { | |||
605 | */ | 622 | */ |
606 | int kgdb_arch_init(void) | 623 | int kgdb_arch_init(void) |
607 | { | 624 | { |
625 | return register_die_notifier(&kgdb_notifier); | ||
626 | } | ||
627 | |||
628 | void kgdb_arch_late(void) | ||
629 | { | ||
608 | int i, cpu; | 630 | int i, cpu; |
609 | int ret; | ||
610 | struct perf_event_attr attr; | 631 | struct perf_event_attr attr; |
611 | struct perf_event **pevent; | 632 | struct perf_event **pevent; |
612 | 633 | ||
613 | ret = register_die_notifier(&kgdb_notifier); | ||
614 | if (ret != 0) | ||
615 | return ret; | ||
616 | /* | 634 | /* |
617 | * Pre-allocate the hw breakpoint structions in the non-atomic | 635 | * Pre-allocate the hw breakpoint structions in the non-atomic |
618 | * portion of kgdb because this operation requires mutexs to | 636 | * portion of kgdb because this operation requires mutexs to |
@@ -624,12 +642,15 @@ int kgdb_arch_init(void) | |||
624 | attr.bp_type = HW_BREAKPOINT_W; | 642 | attr.bp_type = HW_BREAKPOINT_W; |
625 | attr.disabled = 1; | 643 | attr.disabled = 1; |
626 | for (i = 0; i < 4; i++) { | 644 | for (i = 0; i < 4; i++) { |
645 | if (breakinfo[i].pev) | ||
646 | continue; | ||
627 | breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL); | 647 | breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL); |
628 | if (IS_ERR(breakinfo[i].pev)) { | 648 | if (IS_ERR(breakinfo[i].pev)) { |
629 | printk(KERN_ERR "kgdb: Could not allocate hw breakpoints\n"); | 649 | printk(KERN_ERR "kgdb: Could not allocate hw" |
650 | "breakpoints\nDisabling the kernel debugger\n"); | ||
630 | breakinfo[i].pev = NULL; | 651 | breakinfo[i].pev = NULL; |
631 | kgdb_arch_exit(); | 652 | kgdb_arch_exit(); |
632 | return -1; | 653 | return; |
633 | } | 654 | } |
634 | for_each_online_cpu(cpu) { | 655 | for_each_online_cpu(cpu) { |
635 | pevent = per_cpu_ptr(breakinfo[i].pev, cpu); | 656 | pevent = per_cpu_ptr(breakinfo[i].pev, cpu); |
@@ -640,7 +661,6 @@ int kgdb_arch_init(void) | |||
640 | } | 661 | } |
641 | } | 662 | } |
642 | } | 663 | } |
643 | return ret; | ||
644 | } | 664 | } |
645 | 665 | ||
646 | /** | 666 | /** |
@@ -690,6 +710,11 @@ unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs) | |||
690 | return instruction_pointer(regs); | 710 | return instruction_pointer(regs); |
691 | } | 711 | } |
692 | 712 | ||
713 | void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) | ||
714 | { | ||
715 | regs->ip = ip; | ||
716 | } | ||
717 | |||
693 | struct kgdb_arch arch_kgdb_ops = { | 718 | struct kgdb_arch arch_kgdb_ops = { |
694 | /* Breakpoint instruction: */ | 719 | /* Breakpoint instruction: */ |
695 | .gdb_bpt_instr = { 0xcc }, | 720 | .gdb_bpt_instr = { 0xcc }, |