|
@@ -182,6 +182,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 +359,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;
|
|
|
|
|
@@ -269,6 +386,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;
|
|
|
}
|
|
|
|
|
@@ -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,
|
|
|
};
|