MN10300: Use KGDB
authorDavid Howells <dhowells@redhat.com>
Fri, 18 Mar 2011 16:54:31 +0000 (16:54 +0000)
committerDavid Howells <dhowells@redhat.com>
Fri, 18 Mar 2011 16:54:31 +0000 (16:54 +0000)
arch/mn10300/Kconfig
arch/mn10300/Kconfig.debug
arch/mn10300/include/asm/kgdb.h [new file with mode: 0644]
arch/mn10300/include/asm/smp.h
arch/mn10300/kernel/Makefile
arch/mn10300/kernel/kgdb.c [new file with mode: 0644]

index 6e9cac9700244cce04ea53c73fe36051558820bc..d8ab97a73db288db6b601fd13fd2baaa278f6809 100644 (file)
@@ -3,6 +3,8 @@ config MN10300
        select HAVE_OPROFILE
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_HARDIRQS_NO_DEPRECATED
+       select HAVE_ARCH_TRACEHOOK
+       select HAVE_ARCH_KGDB
 
 config AM33_2
        def_bool n
index 87adb8f434ee7da04f06584df0d18496591fcdbe..c2fa3a8f9eef9339c473a11321679993ea65dc42 100644 (file)
@@ -153,4 +153,4 @@ endmenu
 
 config KERNEL_DEBUGGER
        def_bool y
-       depends on GDBSTUB
+       depends on GDBSTUB || KGDB
diff --git a/arch/mn10300/include/asm/kgdb.h b/arch/mn10300/include/asm/kgdb.h
new file mode 100644 (file)
index 0000000..eb245f1
--- /dev/null
@@ -0,0 +1,81 @@
+/* Kernel debugger for MN10300
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _ASM_KGDB_H
+#define _ASM_KGDB_H
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound
+ * buffers at least NUMREGBYTES*2 are needed for register packets
+ * Longer buffer is needed to list all threads
+ */
+#define BUFMAX                 1024
+
+/*
+ * Note that this register image is in a different order than the register
+ * image that Linux produces at interrupt time.
+ */
+enum regnames {
+       GDB_FR_D0               = 0,
+       GDB_FR_D1               = 1,
+       GDB_FR_D2               = 2,
+       GDB_FR_D3               = 3,
+       GDB_FR_A0               = 4,
+       GDB_FR_A1               = 5,
+       GDB_FR_A2               = 6,
+       GDB_FR_A3               = 7,
+
+       GDB_FR_SP               = 8,
+       GDB_FR_PC               = 9,
+       GDB_FR_MDR              = 10,
+       GDB_FR_EPSW             = 11,
+       GDB_FR_LIR              = 12,
+       GDB_FR_LAR              = 13,
+       GDB_FR_MDRQ             = 14,
+
+       GDB_FR_E0               = 15,
+       GDB_FR_E1               = 16,
+       GDB_FR_E2               = 17,
+       GDB_FR_E3               = 18,
+       GDB_FR_E4               = 19,
+       GDB_FR_E5               = 20,
+       GDB_FR_E6               = 21,
+       GDB_FR_E7               = 22,
+
+       GDB_FR_SSP              = 23,
+       GDB_FR_MSP              = 24,
+       GDB_FR_USP              = 25,
+       GDB_FR_MCRH             = 26,
+       GDB_FR_MCRL             = 27,
+       GDB_FR_MCVF             = 28,
+
+       GDB_FR_FPCR             = 29,
+       GDB_FR_DUMMY0           = 30,
+       GDB_FR_DUMMY1           = 31,
+
+       GDB_FR_FS0              = 32,
+
+       GDB_FR_SIZE             = 64,
+};
+
+#define GDB_ORIG_D0            41
+#define NUMREGBYTES            (GDB_FR_SIZE*4)
+
+static inline void arch_kgdb_breakpoint(void)
+{
+       asm(".globl __arch_kgdb_breakpoint; __arch_kgdb_breakpoint: break");
+}
+extern u8 __arch_kgdb_breakpoint;
+
+#define BREAK_INSTR_SIZE       1
+#define CACHE_FLUSH_IS_SAFE    1
+
+#endif /* _ASM_KGDB_H */
index e3d13a899855bb93baff842423505071bdd4081c..6745dbe649441d906cc76de00df7c496961ba1d7 100644 (file)
@@ -62,8 +62,9 @@
  * An alternate way of dealing with this could be to use the EPSW.S bits to
  * cache this information for systems with up to four CPUs.
  */
+#define arch_smp_processor_id()        (CPUID)
 #if 0
-#define raw_smp_processor_id() (CPUID)
+#define raw_smp_processor_id() (arch_smp_processor_id())
 #else
 #define raw_smp_processor_id() (current_thread_info()->cpu)
 #endif
index 48ab4017434dedeea87feafe661e6b3a1f87a830..47ed30fe8178c45c5d99f3ec490feb41770e7c5c 100644 (file)
@@ -25,3 +25,4 @@ obj-$(CONFIG_MN10300_RTC) += rtc.o
 obj-$(CONFIG_PROFILE) += profile.o profile-low.o
 obj-$(CONFIG_MODULES) += module.o
 obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KGDB) += kgdb.o
diff --git a/arch/mn10300/kernel/kgdb.c b/arch/mn10300/kernel/kgdb.c
new file mode 100644 (file)
index 0000000..7d575f5
--- /dev/null
@@ -0,0 +1,202 @@
+/* kgdb support for MN10300
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/ptrace.h>
+#include <linux/kgdb.h>
+#include <linux/uaccess.h>
+#include <unit/leds.h>
+#include <unit/serial.h>
+#include <asm/debugger.h>
+#include <asm/serial-regs.h>
+#include "internal.h"
+
+/*
+ * Copy kernel exception frame registers to the GDB register file
+ */
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       unsigned long ssp = (unsigned long) (regs + 1);
+
+       gdb_regs[GDB_FR_D0]     = regs->d0;
+       gdb_regs[GDB_FR_D1]     = regs->d1;
+       gdb_regs[GDB_FR_D2]     = regs->d2;
+       gdb_regs[GDB_FR_D3]     = regs->d3;
+       gdb_regs[GDB_FR_A0]     = regs->a0;
+       gdb_regs[GDB_FR_A1]     = regs->a1;
+       gdb_regs[GDB_FR_A2]     = regs->a2;
+       gdb_regs[GDB_FR_A3]     = regs->a3;
+       gdb_regs[GDB_FR_SP]     = (regs->epsw & EPSW_nSL) ? regs->sp : ssp;
+       gdb_regs[GDB_FR_PC]     = regs->pc;
+       gdb_regs[GDB_FR_MDR]    = regs->mdr;
+       gdb_regs[GDB_FR_EPSW]   = regs->epsw;
+       gdb_regs[GDB_FR_LIR]    = regs->lir;
+       gdb_regs[GDB_FR_LAR]    = regs->lar;
+       gdb_regs[GDB_FR_MDRQ]   = regs->mdrq;
+       gdb_regs[GDB_FR_E0]     = regs->e0;
+       gdb_regs[GDB_FR_E1]     = regs->e1;
+       gdb_regs[GDB_FR_E2]     = regs->e2;
+       gdb_regs[GDB_FR_E3]     = regs->e3;
+       gdb_regs[GDB_FR_E4]     = regs->e4;
+       gdb_regs[GDB_FR_E5]     = regs->e5;
+       gdb_regs[GDB_FR_E6]     = regs->e6;
+       gdb_regs[GDB_FR_E7]     = regs->e7;
+       gdb_regs[GDB_FR_SSP]    = ssp;
+       gdb_regs[GDB_FR_MSP]    = 0;
+       gdb_regs[GDB_FR_USP]    = regs->sp;
+       gdb_regs[GDB_FR_MCRH]   = regs->mcrh;
+       gdb_regs[GDB_FR_MCRL]   = regs->mcrl;
+       gdb_regs[GDB_FR_MCVF]   = regs->mcvf;
+       gdb_regs[GDB_FR_DUMMY0] = 0;
+       gdb_regs[GDB_FR_DUMMY1] = 0;
+       gdb_regs[GDB_FR_FS0]    = 0;
+}
+
+/*
+ * Extracts kernel SP/PC values understandable by gdb from the values
+ * saved by switch_to().
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+       gdb_regs[GDB_FR_SSP]    = p->thread.sp;
+       gdb_regs[GDB_FR_PC]     = p->thread.pc;
+       gdb_regs[GDB_FR_A3]     = p->thread.a3;
+       gdb_regs[GDB_FR_USP]    = p->thread.usp;
+       gdb_regs[GDB_FR_FPCR]   = p->thread.fpu_state.fpcr;
+}
+
+/*
+ * Fill kernel exception frame registers from the GDB register file
+ */
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+       regs->d0        = gdb_regs[GDB_FR_D0];
+       regs->d1        = gdb_regs[GDB_FR_D1];
+       regs->d2        = gdb_regs[GDB_FR_D2];
+       regs->d3        = gdb_regs[GDB_FR_D3];
+       regs->a0        = gdb_regs[GDB_FR_A0];
+       regs->a1        = gdb_regs[GDB_FR_A1];
+       regs->a2        = gdb_regs[GDB_FR_A2];
+       regs->a3        = gdb_regs[GDB_FR_A3];
+       regs->sp        = gdb_regs[GDB_FR_SP];
+       regs->pc        = gdb_regs[GDB_FR_PC];
+       regs->mdr       = gdb_regs[GDB_FR_MDR];
+       regs->epsw      = gdb_regs[GDB_FR_EPSW];
+       regs->lir       = gdb_regs[GDB_FR_LIR];
+       regs->lar       = gdb_regs[GDB_FR_LAR];
+       regs->mdrq      = gdb_regs[GDB_FR_MDRQ];
+       regs->e0        = gdb_regs[GDB_FR_E0];
+       regs->e1        = gdb_regs[GDB_FR_E1];
+       regs->e2        = gdb_regs[GDB_FR_E2];
+       regs->e3        = gdb_regs[GDB_FR_E3];
+       regs->e4        = gdb_regs[GDB_FR_E4];
+       regs->e5        = gdb_regs[GDB_FR_E5];
+       regs->e6        = gdb_regs[GDB_FR_E6];
+       regs->e7        = gdb_regs[GDB_FR_E7];
+       regs->sp        = gdb_regs[GDB_FR_SSP];
+       /* gdb_regs[GDB_FR_MSP]; */
+       // regs->usp    = gdb_regs[GDB_FR_USP];
+       regs->mcrh      = gdb_regs[GDB_FR_MCRH];
+       regs->mcrl      = gdb_regs[GDB_FR_MCRL];
+       regs->mcvf      = gdb_regs[GDB_FR_MCVF];
+       /* gdb_regs[GDB_FR_DUMMY0]; */
+       /* gdb_regs[GDB_FR_DUMMY1]; */
+
+       // regs->fpcr   = gdb_regs[GDB_FR_FPCR];
+       // regs->fs0    = gdb_regs[GDB_FR_FS0];
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+       .gdb_bpt_instr  = { 0xff },
+       .flags          = KGDB_HW_BREAKPOINT,
+};
+
+/*
+ * Handle unknown packets and [Ccs] packets
+ */
+int kgdb_arch_handle_exception(int vector, int signo, int err_code,
+                              char *remcom_in_buffer, char *remcom_out_buffer,
+                              struct pt_regs *regs)
+{
+       long addr;
+       char *ptr;
+
+       switch (remcom_in_buffer[0]) {
+       case 'c':
+               if (kgdb_contthread && kgdb_contthread != current) {
+                       strcpy(remcom_out_buffer, "E00");
+                       break;
+               }
+
+               kgdb_contthread = NULL;
+
+               /* try to read optional parameter, pc unchanged if no parm */
+               ptr = &remcom_in_buffer[1];
+               if (kgdb_hex2long(&ptr, &addr))
+                       regs->pc = addr;
+               return 0;
+
+       case 's':
+               break; /* we don't do hardware single stepping */
+       }
+       return -1; /* this means that we do not want to exit from the handler */
+}
+
+/*
+ * Handle event interception
+ * - returns 0 if the exception should be skipped, -ERROR otherwise.
+ */
+int debugger_intercept(enum exception_code excep, int signo, int si_code,
+                      struct pt_regs *regs)
+{
+       int ret;
+
+       ret = kgdb_handle_exception(excep, signo, si_code, regs);
+
+       debugger_local_cache_flushinv();
+
+       return ret;
+}
+
+/*
+ * Determine if we've hit a debugger special breakpoint
+ */
+int at_debugger_breakpoint(struct pt_regs *regs)
+{
+       return regs->pc == (unsigned long)&__arch_kgdb_breakpoint;
+}
+
+/*
+ * Initialise kgdb
+ */
+int kgdb_arch_init(void)
+{
+       return 0;
+}
+
+/*
+ * Do something, perhaps, but don't know what.
+ */
+void kgdb_arch_exit(void)
+{
+}
+
+#ifdef CONFIG_SMP
+void debugger_nmi_interrupt(struct pt_regs *regs, enum exception_code code)
+{
+       kgdb_nmicallback(arch_smp_processor_id(), regs);
+       debugger_local_cache_flushinv();
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+       smp_jump_to_debugger();
+}
+#endif