]> err.no Git - linux-2.6/blobdiff - arch/x86/kernel/kgdb.c
Merge branch 'linus' into x86/urgent
[linux-2.6] / arch / x86 / kernel / kgdb.c
index 5d7a21119bf83af40387da7dfdc386e3f9cc3e7d..f47f0eb886b8ddeab27ec8a8fd319801c45ef503 100644 (file)
 #include <asm/apicdef.h>
 #include <asm/system.h>
 
-#ifdef CONFIG_X86_32
-# include <mach_ipi.h>
-#else
-# include <asm/mach_apic.h>
-#endif
+#include <mach_ipi.h>
 
 /*
  * Put the error code here just in case the user cares:
@@ -182,6 +178,122 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
 #endif
 }
 
+static struct hw_breakpoint {
+       unsigned                enabled;
+       unsigned                type;
+       unsigned                len;
+       unsigned long           addr;
+} breakinfo[4];
+
+static void kgdb_correct_hw_break(void)
+{
+       unsigned long dr7;
+       int correctit = 0;
+       int breakbit;
+       int breakno;
+
+       get_debugreg(dr7, 7);
+       for (breakno = 0; breakno < 4; breakno++) {
+               breakbit = 2 << (breakno << 1);
+               if (!(dr7 & breakbit) && breakinfo[breakno].enabled) {
+                       correctit = 1;
+                       dr7 |= breakbit;
+                       dr7 &= ~(0xf0000 << (breakno << 2));
+                       dr7 |= ((breakinfo[breakno].len << 2) |
+                                breakinfo[breakno].type) <<
+                              ((breakno << 2) + 16);
+                       if (breakno >= 0 && breakno <= 3)
+                               set_debugreg(breakinfo[breakno].addr, breakno);
+
+               } else {
+                       if ((dr7 & breakbit) && !breakinfo[breakno].enabled) {
+                               correctit = 1;
+                               dr7 &= ~breakbit;
+                               dr7 &= ~(0xf0000 << (breakno << 2));
+                       }
+               }
+       }
+       if (correctit)
+               set_debugreg(dr7, 7);
+}
+
+static int
+kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
+{
+       int i;
+
+       for (i = 0; i < 4; i++)
+               if (breakinfo[i].addr == addr && breakinfo[i].enabled)
+                       break;
+       if (i == 4)
+               return -1;
+
+       breakinfo[i].enabled = 0;
+
+       return 0;
+}
+
+static void kgdb_remove_all_hw_break(void)
+{
+       int i;
+
+       for (i = 0; i < 4; i++)
+               memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint));
+}
+
+static int
+kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
+{
+       unsigned type;
+       int i;
+
+       for (i = 0; i < 4; i++)
+               if (!breakinfo[i].enabled)
+                       break;
+       if (i == 4)
+               return -1;
+
+       switch (bptype) {
+       case BP_HARDWARE_BREAKPOINT:
+               type = 0;
+               len  = 1;
+               break;
+       case BP_WRITE_WATCHPOINT:
+               type = 1;
+               break;
+       case BP_ACCESS_WATCHPOINT:
+               type = 3;
+               break;
+       default:
+               return -1;
+       }
+
+       if (len == 1 || len == 2 || len == 4)
+               breakinfo[i].len  = len - 1;
+       else
+               return -1;
+
+       breakinfo[i].enabled = 1;
+       breakinfo[i].addr = addr;
+       breakinfo[i].type = type;
+
+       return 0;
+}
+
+/**
+ *     kgdb_disable_hw_debug - Disable hardware debugging while we in kgdb.
+ *     @regs: Current &struct pt_regs.
+ *
+ *     This function will be called if the particular architecture must
+ *     disable hardware debugging while it is processing gdb packets or
+ *     handling exception.
+ */
+void kgdb_disable_hw_debug(struct pt_regs *regs)
+{
+       /* Disable hardware debugging while we are in kgdb: */
+       set_debugreg(0UL, 7);
+}
+
 /**
  *     kgdb_post_primary_code - Save error vector/code numbers.
  *     @regs: Original pt_regs.
@@ -243,6 +355,7 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
                               struct pt_regs *linux_regs)
 {
        unsigned long addr;
+       unsigned long dr6;
        char *ptr;
        int newPC;
 
@@ -253,15 +366,17 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
                ptr = &remcomInBuffer[1];
                if (kgdb_hex2long(&ptr, &addr))
                        linux_regs->ip = addr;
+       case 'D':
+       case 'k':
                newPC = linux_regs->ip;
 
                /* clear the trace bit */
-               linux_regs->flags &= ~TF_MASK;
+               linux_regs->flags &= ~X86_EFLAGS_TF;
                atomic_set(&kgdb_cpu_doing_single_step, -1);
 
                /* set the trace bit if we're stepping */
                if (remcomInBuffer[0] == 's') {
-                       linux_regs->flags |= TF_MASK;
+                       linux_regs->flags |= X86_EFLAGS_TF;
                        kgdb_single_step = 1;
                        if (kgdb_contthread) {
                                atomic_set(&kgdb_cpu_doing_single_step,
@@ -269,6 +384,22 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
                        }
                }
 
+               get_debugreg(dr6, 6);
+               if (!(dr6 & 0x4000)) {
+                       int breakno;
+
+                       for (breakno = 0; breakno < 4; breakno++) {
+                               if (dr6 & (1 << breakno) &&
+                                   breakinfo[breakno].type == 0) {
+                                       /* Set restore flag: */
+                                       linux_regs->flags |= X86_EFLAGS_RF;
+                                       break;
+                               }
+                       }
+               }
+               set_debugreg(0UL, 6);
+               kgdb_correct_hw_break();
+
                return 0;
        }
 
@@ -347,6 +478,8 @@ static int __kgdb_notify(struct die_args *args, unsigned long cmd)
        if (kgdb_handle_exception(args->trapnr, args->signr, args->err, regs))
                return NOTIFY_DONE;
 
+       /* Must touch watchdog before return to normal operation */
+       touch_nmi_watchdog();
        return NOTIFY_STOP;
 }
 
@@ -426,4 +559,9 @@ unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
 struct kgdb_arch arch_kgdb_ops = {
        /* Breakpoint instruction: */
        .gdb_bpt_instr          = { 0xcc },
+       .flags                  = KGDB_HW_BREAKPOINT,
+       .set_hw_breakpoint      = kgdb_set_hw_break,
+       .remove_hw_breakpoint   = kgdb_remove_hw_break,
+       .remove_all_hw_break    = kgdb_remove_all_hw_break,
+       .correct_hw_break       = kgdb_correct_hw_break,
 };