Commit e460d64405c04581e42aa9cbae76815a2d4e9abe
1 parent
67ddb4052d
Exists in
master
and in
39 other branches
MN10300: Use KGDB
Showing 6 changed files with 289 additions and 2 deletions Side-by-side Diff
arch/mn10300/Kconfig
arch/mn10300/Kconfig.debug
arch/mn10300/include/asm/kgdb.h
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 */ |
arch/mn10300/include/asm/smp.h
... | ... | @@ -62,8 +62,9 @@ |
62 | 62 | * An alternate way of dealing with this could be to use the EPSW.S bits to |
63 | 63 | * cache this information for systems with up to four CPUs. |
64 | 64 | */ |
65 | +#define arch_smp_processor_id() (CPUID) | |
65 | 66 | #if 0 |
66 | -#define raw_smp_processor_id() (CPUID) | |
67 | +#define raw_smp_processor_id() (arch_smp_processor_id()) | |
67 | 68 | #else |
68 | 69 | #define raw_smp_processor_id() (current_thread_info()->cpu) |
69 | 70 | #endif |
arch/mn10300/kernel/Makefile
arch/mn10300/kernel/kgdb.c
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 |