summaryrefslogtreecommitdiff
path: root/arch/mn10300
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2011-03-18 19:54:31 +0300
committerDavid Howells <dhowells@redhat.com>2011-03-18 19:54:31 +0300
commite460d64405c04581e42aa9cbae76815a2d4e9abe (patch)
tree5c3deddaaf6d7883d6d39344b5a00857299f115b /arch/mn10300
parent67ddb4052daac9d449caf2643ac365d42a04219a (diff)
downloadlinux-e460d64405c04581e42aa9cbae76815a2d4e9abe.tar.xz
MN10300: Use KGDB
Diffstat (limited to 'arch/mn10300')
-rw-r--r--arch/mn10300/Kconfig2
-rw-r--r--arch/mn10300/Kconfig.debug2
-rw-r--r--arch/mn10300/include/asm/kgdb.h81
-rw-r--r--arch/mn10300/include/asm/smp.h3
-rw-r--r--arch/mn10300/kernel/Makefile1
-rw-r--r--arch/mn10300/kernel/kgdb.c202
6 files changed, 289 insertions, 2 deletions
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig
index 6e9cac970024..d8ab97a73db2 100644
--- a/arch/mn10300/Kconfig
+++ b/arch/mn10300/Kconfig
@@ -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
diff --git a/arch/mn10300/Kconfig.debug b/arch/mn10300/Kconfig.debug
index 87adb8f434ee..c2fa3a8f9eef 100644
--- a/arch/mn10300/Kconfig.debug
+++ b/arch/mn10300/Kconfig.debug
@@ -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
index 000000000000..eb245f18a708
--- /dev/null
+++ b/arch/mn10300/include/asm/kgdb.h
@@ -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 */
diff --git a/arch/mn10300/include/asm/smp.h b/arch/mn10300/include/asm/smp.h
index e3d13a899855..6745dbe64944 100644
--- a/arch/mn10300/include/asm/smp.h
+++ b/arch/mn10300/include/asm/smp.h
@@ -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
diff --git a/arch/mn10300/kernel/Makefile b/arch/mn10300/kernel/Makefile
index 48ab4017434d..47ed30fe8178 100644
--- a/arch/mn10300/kernel/Makefile
+++ b/arch/mn10300/kernel/Makefile
@@ -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
index 000000000000..7d575f553099
--- /dev/null
+++ b/arch/mn10300/kernel/kgdb.c
@@ -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