diff options
| author | David Howells <dhowells@redhat.com> | 2011-03-18 12:54:31 -0400 |
|---|---|---|
| committer | David Howells <dhowells@redhat.com> | 2011-03-18 12:54:31 -0400 |
| commit | e460d64405c04581e42aa9cbae76815a2d4e9abe (patch) | |
| tree | 5c3deddaaf6d7883d6d39344b5a00857299f115b | |
| parent | 67ddb4052daac9d449caf2643ac365d42a04219a (diff) | |
MN10300: Use KGDB
| -rw-r--r-- | arch/mn10300/Kconfig | 2 | ||||
| -rw-r--r-- | arch/mn10300/Kconfig.debug | 2 | ||||
| -rw-r--r-- | arch/mn10300/include/asm/kgdb.h | 81 | ||||
| -rw-r--r-- | arch/mn10300/include/asm/smp.h | 3 | ||||
| -rw-r--r-- | arch/mn10300/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/mn10300/kernel/kgdb.c | 202 |
6 files changed, 289 insertions, 2 deletions
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index 6e9cac97002..d8ab97a73db 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig | |||
| @@ -3,6 +3,8 @@ config MN10300 | |||
| 3 | select HAVE_OPROFILE | 3 | select HAVE_OPROFILE |
| 4 | select HAVE_GENERIC_HARDIRQS | 4 | select HAVE_GENERIC_HARDIRQS |
| 5 | select GENERIC_HARDIRQS_NO_DEPRECATED | 5 | select GENERIC_HARDIRQS_NO_DEPRECATED |
| 6 | select HAVE_ARCH_TRACEHOOK | ||
| 7 | select HAVE_ARCH_KGDB | ||
| 6 | 8 | ||
| 7 | config AM33_2 | 9 | config AM33_2 |
| 8 | def_bool n | 10 | def_bool n |
diff --git a/arch/mn10300/Kconfig.debug b/arch/mn10300/Kconfig.debug index 87adb8f434e..c2fa3a8f9ee 100644 --- a/arch/mn10300/Kconfig.debug +++ b/arch/mn10300/Kconfig.debug | |||
| @@ -153,4 +153,4 @@ endmenu | |||
| 153 | 153 | ||
| 154 | config KERNEL_DEBUGGER | 154 | config KERNEL_DEBUGGER |
| 155 | def_bool y | 155 | def_bool y |
| 156 | depends on GDBSTUB | 156 | 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 00000000000..eb245f18a70 --- /dev/null +++ b/arch/mn10300/include/asm/kgdb.h | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | /* Kernel debugger for MN10300 | ||
| 2 | * | ||
| 3 | * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public Licence | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the Licence, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef _ASM_KGDB_H | ||
| 13 | #define _ASM_KGDB_H | ||
| 14 | |||
| 15 | /* | ||
| 16 | * BUFMAX defines the maximum number of characters in inbound/outbound | ||
| 17 | * buffers at least NUMREGBYTES*2 are needed for register packets | ||
| 18 | * Longer buffer is needed to list all threads | ||
| 19 | */ | ||
| 20 | #define BUFMAX 1024 | ||
| 21 | |||
| 22 | /* | ||
| 23 | * Note that this register image is in a different order than the register | ||
| 24 | * image that Linux produces at interrupt time. | ||
| 25 | */ | ||
| 26 | enum regnames { | ||
| 27 | GDB_FR_D0 = 0, | ||
| 28 | GDB_FR_D1 = 1, | ||
| 29 | GDB_FR_D2 = 2, | ||
| 30 | GDB_FR_D3 = 3, | ||
| 31 | GDB_FR_A0 = 4, | ||
| 32 | GDB_FR_A1 = 5, | ||
| 33 | GDB_FR_A2 = 6, | ||
| 34 | GDB_FR_A3 = 7, | ||
| 35 | |||
| 36 | GDB_FR_SP = 8, | ||
| 37 | GDB_FR_PC = 9, | ||
| 38 | GDB_FR_MDR = 10, | ||
| 39 | GDB_FR_EPSW = 11, | ||
| 40 | GDB_FR_LIR = 12, | ||
| 41 | GDB_FR_LAR = 13, | ||
| 42 | GDB_FR_MDRQ = 14, | ||
| 43 | |||
| 44 | GDB_FR_E0 = 15, | ||
| 45 | GDB_FR_E1 = 16, | ||
| 46 | GDB_FR_E2 = 17, | ||
| 47 | GDB_FR_E3 = 18, | ||
| 48 | GDB_FR_E4 = 19, | ||
| 49 | GDB_FR_E5 = 20, | ||
| 50 | GDB_FR_E6 = 21, | ||
| 51 | GDB_FR_E7 = 22, | ||
| 52 | |||
| 53 | GDB_FR_SSP = 23, | ||
| 54 | GDB_FR_MSP = 24, | ||
| 55 | GDB_FR_USP = 25, | ||
| 56 | GDB_FR_MCRH = 26, | ||
| 57 | GDB_FR_MCRL = 27, | ||
| 58 | GDB_FR_MCVF = 28, | ||
| 59 | |||
| 60 | GDB_FR_FPCR = 29, | ||
| 61 | GDB_FR_DUMMY0 = 30, | ||
| 62 | GDB_FR_DUMMY1 = 31, | ||
| 63 | |||
| 64 | GDB_FR_FS0 = 32, | ||
| 65 | |||
| 66 | GDB_FR_SIZE = 64, | ||
| 67 | }; | ||
| 68 | |||
| 69 | #define GDB_ORIG_D0 41 | ||
| 70 | #define NUMREGBYTES (GDB_FR_SIZE*4) | ||
| 71 | |||
| 72 | static inline void arch_kgdb_breakpoint(void) | ||
| 73 | { | ||
| 74 | asm(".globl __arch_kgdb_breakpoint; __arch_kgdb_breakpoint: break"); | ||
| 75 | } | ||
| 76 | extern u8 __arch_kgdb_breakpoint; | ||
| 77 | |||
| 78 | #define BREAK_INSTR_SIZE 1 | ||
| 79 | #define CACHE_FLUSH_IS_SAFE 1 | ||
| 80 | |||
| 81 | #endif /* _ASM_KGDB_H */ | ||
diff --git a/arch/mn10300/include/asm/smp.h b/arch/mn10300/include/asm/smp.h index e3d13a89985..6745dbe6494 100644 --- a/arch/mn10300/include/asm/smp.h +++ b/arch/mn10300/include/asm/smp.h | |||
| @@ -62,8 +62,9 @@ | |||
| 62 | * An alternate way of dealing with this could be to use the EPSW.S bits to | 62 | * An alternate way of dealing with this could be to use the EPSW.S bits to |
| 63 | * cache this information for systems with up to four CPUs. | 63 | * cache this information for systems with up to four CPUs. |
| 64 | */ | 64 | */ |
| 65 | #define arch_smp_processor_id() (CPUID) | ||
| 65 | #if 0 | 66 | #if 0 |
| 66 | #define raw_smp_processor_id() (CPUID) | 67 | #define raw_smp_processor_id() (arch_smp_processor_id()) |
| 67 | #else | 68 | #else |
| 68 | #define raw_smp_processor_id() (current_thread_info()->cpu) | 69 | #define raw_smp_processor_id() (current_thread_info()->cpu) |
| 69 | #endif | 70 | #endif |
diff --git a/arch/mn10300/kernel/Makefile b/arch/mn10300/kernel/Makefile index 48ab4017434..47ed30fe817 100644 --- a/arch/mn10300/kernel/Makefile +++ b/arch/mn10300/kernel/Makefile | |||
| @@ -25,3 +25,4 @@ obj-$(CONFIG_MN10300_RTC) += rtc.o | |||
| 25 | obj-$(CONFIG_PROFILE) += profile.o profile-low.o | 25 | obj-$(CONFIG_PROFILE) += profile.o profile-low.o |
| 26 | obj-$(CONFIG_MODULES) += module.o | 26 | obj-$(CONFIG_MODULES) += module.o |
| 27 | obj-$(CONFIG_KPROBES) += kprobes.o | 27 | obj-$(CONFIG_KPROBES) += kprobes.o |
| 28 | obj-$(CONFIG_KGDB) += kgdb.o | ||
diff --git a/arch/mn10300/kernel/kgdb.c b/arch/mn10300/kernel/kgdb.c new file mode 100644 index 00000000000..7d575f55309 --- /dev/null +++ b/arch/mn10300/kernel/kgdb.c | |||
| @@ -0,0 +1,202 @@ | |||
| 1 | /* kgdb support for MN10300 | ||
| 2 | * | ||
| 3 | * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public Licence | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the Licence, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/ptrace.h> | ||
| 13 | #include <linux/kgdb.h> | ||
| 14 | #include <linux/uaccess.h> | ||
| 15 | #include <unit/leds.h> | ||
| 16 | #include <unit/serial.h> | ||
| 17 | #include <asm/debugger.h> | ||
| 18 | #include <asm/serial-regs.h> | ||
| 19 | #include "internal.h" | ||
| 20 | |||
| 21 | /* | ||
| 22 | * Copy kernel exception frame registers to the GDB register file | ||
| 23 | */ | ||
| 24 | void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
| 25 | { | ||
| 26 | unsigned long ssp = (unsigned long) (regs + 1); | ||
| 27 | |||
| 28 | gdb_regs[GDB_FR_D0] = regs->d0; | ||
| 29 | gdb_regs[GDB_FR_D1] = regs->d1; | ||
| 30 | gdb_regs[GDB_FR_D2] = regs->d2; | ||
| 31 | gdb_regs[GDB_FR_D3] = regs->d3; | ||
| 32 | gdb_regs[GDB_FR_A0] = regs->a0; | ||
| 33 | gdb_regs[GDB_FR_A1] = regs->a1; | ||
| 34 | gdb_regs[GDB_FR_A2] = regs->a2; | ||
| 35 | gdb_regs[GDB_FR_A3] = regs->a3; | ||
| 36 | gdb_regs[GDB_FR_SP] = (regs->epsw & EPSW_nSL) ? regs->sp : ssp; | ||
| 37 | gdb_regs[GDB_FR_PC] = regs->pc; | ||
| 38 | gdb_regs[GDB_FR_MDR] = regs->mdr; | ||
| 39 | gdb_regs[GDB_FR_EPSW] = regs->epsw; | ||
| 40 | gdb_regs[GDB_FR_LIR] = regs->lir; | ||
| 41 | gdb_regs[GDB_FR_LAR] = regs->lar; | ||
| 42 | gdb_regs[GDB_FR_MDRQ] = regs->mdrq; | ||
| 43 | gdb_regs[GDB_FR_E0] = regs->e0; | ||
| 44 | gdb_regs[GDB_FR_E1] = regs->e1; | ||
| 45 | gdb_regs[GDB_FR_E2] = regs->e2; | ||
| 46 | gdb_regs[GDB_FR_E3] = regs->e3; | ||
| 47 | gdb_regs[GDB_FR_E4] = regs->e4; | ||
| 48 | gdb_regs[GDB_FR_E5] = regs->e5; | ||
| 49 | gdb_regs[GDB_FR_E6] = regs->e6; | ||
| 50 | gdb_regs[GDB_FR_E7] = regs->e7; | ||
| 51 | gdb_regs[GDB_FR_SSP] = ssp; | ||
| 52 | gdb_regs[GDB_FR_MSP] = 0; | ||
| 53 | gdb_regs[GDB_FR_USP] = regs->sp; | ||
| 54 | gdb_regs[GDB_FR_MCRH] = regs->mcrh; | ||
| 55 | gdb_regs[GDB_FR_MCRL] = regs->mcrl; | ||
| 56 | gdb_regs[GDB_FR_MCVF] = regs->mcvf; | ||
| 57 | gdb_regs[GDB_FR_DUMMY0] = 0; | ||
| 58 | gdb_regs[GDB_FR_DUMMY1] = 0; | ||
| 59 | gdb_regs[GDB_FR_FS0] = 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Extracts kernel SP/PC values understandable by gdb from the values | ||
| 64 | * saved by switch_to(). | ||
| 65 | */ | ||
| 66 | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) | ||
| 67 | { | ||
| 68 | gdb_regs[GDB_FR_SSP] = p->thread.sp; | ||
| 69 | gdb_regs[GDB_FR_PC] = p->thread.pc; | ||
| 70 | gdb_regs[GDB_FR_A3] = p->thread.a3; | ||
| 71 | gdb_regs[GDB_FR_USP] = p->thread.usp; | ||
| 72 | gdb_regs[GDB_FR_FPCR] = p->thread.fpu_state.fpcr; | ||
| 73 | } | ||
| 74 | |||
| 75 | /* | ||
| 76 | * Fill kernel exception frame registers from the GDB register file | ||
| 77 | */ | ||
| 78 | void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
| 79 | { | ||
| 80 | regs->d0 = gdb_regs[GDB_FR_D0]; | ||
| 81 | regs->d1 = gdb_regs[GDB_FR_D1]; | ||
| 82 | regs->d2 = gdb_regs[GDB_FR_D2]; | ||
| 83 | regs->d3 = gdb_regs[GDB_FR_D3]; | ||
| 84 | regs->a0 = gdb_regs[GDB_FR_A0]; | ||
| 85 | regs->a1 = gdb_regs[GDB_FR_A1]; | ||
| 86 | regs->a2 = gdb_regs[GDB_FR_A2]; | ||
| 87 | regs->a3 = gdb_regs[GDB_FR_A3]; | ||
| 88 | regs->sp = gdb_regs[GDB_FR_SP]; | ||
| 89 | regs->pc = gdb_regs[GDB_FR_PC]; | ||
| 90 | regs->mdr = gdb_regs[GDB_FR_MDR]; | ||
| 91 | regs->epsw = gdb_regs[GDB_FR_EPSW]; | ||
| 92 | regs->lir = gdb_regs[GDB_FR_LIR]; | ||
| 93 | regs->lar = gdb_regs[GDB_FR_LAR]; | ||
| 94 | regs->mdrq = gdb_regs[GDB_FR_MDRQ]; | ||
| 95 | regs->e0 = gdb_regs[GDB_FR_E0]; | ||
| 96 | regs->e1 = gdb_regs[GDB_FR_E1]; | ||
| 97 | regs->e2 = gdb_regs[GDB_FR_E2]; | ||
| 98 | regs->e3 = gdb_regs[GDB_FR_E3]; | ||
| 99 | regs->e4 = gdb_regs[GDB_FR_E4]; | ||
| 100 | regs->e5 = gdb_regs[GDB_FR_E5]; | ||
| 101 | regs->e6 = gdb_regs[GDB_FR_E6]; | ||
| 102 | regs->e7 = gdb_regs[GDB_FR_E7]; | ||
| 103 | regs->sp = gdb_regs[GDB_FR_SSP]; | ||
| 104 | /* gdb_regs[GDB_FR_MSP]; */ | ||
| 105 | // regs->usp = gdb_regs[GDB_FR_USP]; | ||
| 106 | regs->mcrh = gdb_regs[GDB_FR_MCRH]; | ||
| 107 | regs->mcrl = gdb_regs[GDB_FR_MCRL]; | ||
| 108 | regs->mcvf = gdb_regs[GDB_FR_MCVF]; | ||
| 109 | /* gdb_regs[GDB_FR_DUMMY0]; */ | ||
| 110 | /* gdb_regs[GDB_FR_DUMMY1]; */ | ||
| 111 | |||
| 112 | // regs->fpcr = gdb_regs[GDB_FR_FPCR]; | ||
| 113 | // regs->fs0 = gdb_regs[GDB_FR_FS0]; | ||
| 114 | } | ||
| 115 | |||
| 116 | struct kgdb_arch arch_kgdb_ops = { | ||
| 117 | .gdb_bpt_instr = { 0xff }, | ||
| 118 | .flags = KGDB_HW_BREAKPOINT, | ||
| 119 | }; | ||
| 120 | |||
| 121 | /* | ||
| 122 | * Handle unknown packets and [Ccs] packets | ||
| 123 | */ | ||
| 124 | int kgdb_arch_handle_exception(int vector, int signo, int err_code, | ||
| 125 | char *remcom_in_buffer, char *remcom_out_buffer, | ||
| 126 | struct pt_regs *regs) | ||
| 127 | { | ||
| 128 | long addr; | ||
| 129 | char *ptr; | ||
| 130 | |||
| 131 | switch (remcom_in_buffer[0]) { | ||
| 132 | case 'c': | ||
| 133 | if (kgdb_contthread && kgdb_contthread != current) { | ||
| 134 | strcpy(remcom_out_buffer, "E00"); | ||
| 135 | break; | ||
| 136 | } | ||
| 137 | |||
| 138 | kgdb_contthread = NULL; | ||
| 139 | |||
| 140 | /* try to read optional parameter, pc unchanged if no parm */ | ||
| 141 | ptr = &remcom_in_buffer[1]; | ||
| 142 | if (kgdb_hex2long(&ptr, &addr)) | ||
| 143 | regs->pc = addr; | ||
| 144 | return 0; | ||
| 145 | |||
| 146 | case 's': | ||
| 147 | break; /* we don't do hardware single stepping */ | ||
| 148 | } | ||
| 149 | return -1; /* this means that we do not want to exit from the handler */ | ||
| 150 | } | ||
| 151 | |||
| 152 | /* | ||
| 153 | * Handle event interception | ||
| 154 | * - returns 0 if the exception should be skipped, -ERROR otherwise. | ||
| 155 | */ | ||
| 156 | int debugger_intercept(enum exception_code excep, int signo, int si_code, | ||
| 157 | struct pt_regs *regs) | ||
| 158 | { | ||
| 159 | int ret; | ||
| 160 | |||
| 161 | ret = kgdb_handle_exception(excep, signo, si_code, regs); | ||
| 162 | |||
| 163 | debugger_local_cache_flushinv(); | ||
| 164 | |||
| 165 | return ret; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* | ||
| 169 | * Determine if we've hit a debugger special breakpoint | ||
| 170 | */ | ||
| 171 | int at_debugger_breakpoint(struct pt_regs *regs) | ||
| 172 | { | ||
| 173 | return regs->pc == (unsigned long)&__arch_kgdb_breakpoint; | ||
| 174 | } | ||
| 175 | |||
| 176 | /* | ||
| 177 | * Initialise kgdb | ||
| 178 | */ | ||
| 179 | int kgdb_arch_init(void) | ||
| 180 | { | ||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* | ||
| 185 | * Do something, perhaps, but don't know what. | ||
| 186 | */ | ||
| 187 | void kgdb_arch_exit(void) | ||
| 188 | { | ||
| 189 | } | ||
| 190 | |||
| 191 | #ifdef CONFIG_SMP | ||
| 192 | void debugger_nmi_interrupt(struct pt_regs *regs, enum exception_code code) | ||
| 193 | { | ||
| 194 | kgdb_nmicallback(arch_smp_processor_id(), regs); | ||
| 195 | debugger_local_cache_flushinv(); | ||
| 196 | } | ||
| 197 | |||
| 198 | void kgdb_roundup_cpus(unsigned long flags) | ||
| 199 | { | ||
| 200 | smp_jump_to_debugger(); | ||
| 201 | } | ||
| 202 | #endif | ||
