diff options
author | Mischa Jonker <mjonker@synopsys.com> | 2013-01-18 04:42:24 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-02-15 12:46:07 -0500 |
commit | f46121bd26d7957866739313f1e098a682e8d3e4 (patch) | |
tree | 1469839dbb041f325d01428e973e637f3297595e /arch/arc | |
parent | 2e651ea1596b0ee25af4fcdc4cd13cbb33ffc254 (diff) |
ARC: kgdb support
Signed-off-by: Mischa Jonker <mjonker@synopsys.com>
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Cc: Jason Wessel <jason.wessel@windriver.com>
Acked-by: Jason Wessel <jason.wessel@windriver.com>
Diffstat (limited to 'arch/arc')
-rw-r--r-- | arch/arc/Kconfig | 3 | ||||
-rw-r--r-- | arch/arc/include/asm/kgdb.h | 61 | ||||
-rw-r--r-- | arch/arc/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arc/kernel/kgdb.c | 205 | ||||
-rw-r--r-- | arch/arc/kernel/traps.c | 6 |
5 files changed, 275 insertions, 1 deletions
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index f8042835e746..69a939af72c6 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig | |||
@@ -22,6 +22,7 @@ config ARC | |||
22 | select GENERIC_PENDING_IRQ if SMP | 22 | select GENERIC_PENDING_IRQ if SMP |
23 | select GENERIC_SIGALTSTACK | 23 | select GENERIC_SIGALTSTACK |
24 | select GENERIC_SMP_IDLE_THREAD | 24 | select GENERIC_SMP_IDLE_THREAD |
25 | select HAVE_ARCH_KGDB | ||
25 | select HAVE_ARCH_TRACEHOOK | 26 | select HAVE_ARCH_TRACEHOOK |
26 | select HAVE_GENERIC_HARDIRQS | 27 | select HAVE_GENERIC_HARDIRQS |
27 | select HAVE_KPROBES | 28 | select HAVE_KPROBES |
@@ -378,7 +379,7 @@ config ARC_DW2_UNWIND | |||
378 | 379 | ||
379 | config ARC_DBG_TLB_PARANOIA | 380 | config ARC_DBG_TLB_PARANOIA |
380 | bool "Paranoia Checks in Low Level TLB Handlers" | 381 | bool "Paranoia Checks in Low Level TLB Handlers" |
381 | depends on ARC_DBG && !SMP | 382 | depends on ARC_DBG |
382 | default n | 383 | default n |
383 | 384 | ||
384 | config ARC_DBG_TLB_MISS_COUNT | 385 | config ARC_DBG_TLB_MISS_COUNT |
diff --git a/arch/arc/include/asm/kgdb.h b/arch/arc/include/asm/kgdb.h new file mode 100644 index 000000000000..f3c4934f0ca9 --- /dev/null +++ b/arch/arc/include/asm/kgdb.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * kgdb support for ARC | ||
3 | * | ||
4 | * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef __ARC_KGDB_H__ | ||
12 | #define __ARC_KGDB_H__ | ||
13 | |||
14 | #ifdef CONFIG_KGDB | ||
15 | |||
16 | #include <asm/user.h> | ||
17 | |||
18 | /* to ensure compatibility with Linux 2.6.35, we don't implement the get/set | ||
19 | * register API yet */ | ||
20 | #undef DBG_MAX_REG_NUM | ||
21 | |||
22 | #define GDB_MAX_REGS 39 | ||
23 | |||
24 | #define BREAK_INSTR_SIZE 2 | ||
25 | #define CACHE_FLUSH_IS_SAFE 1 | ||
26 | #define NUMREGBYTES (GDB_MAX_REGS * 4) | ||
27 | #define BUFMAX 2048 | ||
28 | |||
29 | static inline void arch_kgdb_breakpoint(void) | ||
30 | { | ||
31 | __asm__ __volatile__ ("trap_s 0x4\n"); | ||
32 | } | ||
33 | |||
34 | extern void kgdb_trap(struct pt_regs *regs, int param); | ||
35 | |||
36 | enum arc700_linux_regnums { | ||
37 | _R0 = 0, | ||
38 | _R1, _R2, _R3, _R4, _R5, _R6, _R7, _R8, _R9, _R10, _R11, _R12, _R13, | ||
39 | _R14, _R15, _R16, _R17, _R18, _R19, _R20, _R21, _R22, _R23, _R24, | ||
40 | _R25, _R26, | ||
41 | _BTA = 27, | ||
42 | _LP_START = 28, | ||
43 | _LP_END = 29, | ||
44 | _LP_COUNT = 30, | ||
45 | _STATUS32 = 31, | ||
46 | _BLINK = 32, | ||
47 | _FP = 33, | ||
48 | __SP = 34, | ||
49 | _EFA = 35, | ||
50 | _RET = 36, | ||
51 | _ORIG_R8 = 37, | ||
52 | _STOP_PC = 38 | ||
53 | }; | ||
54 | |||
55 | #else | ||
56 | static inline void kgdb_trap(struct pt_regs *regs, int param) | ||
57 | { | ||
58 | } | ||
59 | #endif | ||
60 | |||
61 | #endif /* __ARC_KGDB_H__ */ | ||
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 507a33d4a255..6da2b12cf7e3 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile | |||
@@ -17,6 +17,7 @@ obj-$(CONFIG_SMP) += smp.o | |||
17 | obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o | 17 | obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o |
18 | obj-$(CONFIG_KPROBES) += kprobes.o | 18 | obj-$(CONFIG_KPROBES) += kprobes.o |
19 | obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o | 19 | obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o |
20 | obj-$(CONFIG_KGDB) += kgdb.o | ||
20 | 21 | ||
21 | obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o | 22 | obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o |
22 | CFLAGS_fpu.o += -mdpfp | 23 | CFLAGS_fpu.o += -mdpfp |
diff --git a/arch/arc/kernel/kgdb.c b/arch/arc/kernel/kgdb.c new file mode 100644 index 000000000000..2888ba5be47e --- /dev/null +++ b/arch/arc/kernel/kgdb.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * kgdb support for ARC | ||
3 | * | ||
4 | * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kgdb.h> | ||
12 | #include <asm/disasm.h> | ||
13 | #include <asm/cacheflush.h> | ||
14 | |||
15 | static void to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs, | ||
16 | struct callee_regs *cregs) | ||
17 | { | ||
18 | int regno; | ||
19 | |||
20 | for (regno = 0; regno <= 26; regno++) | ||
21 | gdb_regs[_R0 + regno] = get_reg(regno, kernel_regs, cregs); | ||
22 | |||
23 | for (regno = 27; regno < GDB_MAX_REGS; regno++) | ||
24 | gdb_regs[regno] = 0; | ||
25 | |||
26 | gdb_regs[_FP] = kernel_regs->fp; | ||
27 | gdb_regs[__SP] = kernel_regs->sp; | ||
28 | gdb_regs[_BLINK] = kernel_regs->blink; | ||
29 | gdb_regs[_RET] = kernel_regs->ret; | ||
30 | gdb_regs[_STATUS32] = kernel_regs->status32; | ||
31 | gdb_regs[_LP_COUNT] = kernel_regs->lp_count; | ||
32 | gdb_regs[_LP_END] = kernel_regs->lp_end; | ||
33 | gdb_regs[_LP_START] = kernel_regs->lp_start; | ||
34 | gdb_regs[_BTA] = kernel_regs->bta; | ||
35 | gdb_regs[_STOP_PC] = kernel_regs->ret; | ||
36 | } | ||
37 | |||
38 | static void from_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs, | ||
39 | struct callee_regs *cregs) | ||
40 | { | ||
41 | int regno; | ||
42 | |||
43 | for (regno = 0; regno <= 26; regno++) | ||
44 | set_reg(regno, gdb_regs[regno + _R0], kernel_regs, cregs); | ||
45 | |||
46 | kernel_regs->fp = gdb_regs[_FP]; | ||
47 | kernel_regs->sp = gdb_regs[__SP]; | ||
48 | kernel_regs->blink = gdb_regs[_BLINK]; | ||
49 | kernel_regs->ret = gdb_regs[_RET]; | ||
50 | kernel_regs->status32 = gdb_regs[_STATUS32]; | ||
51 | kernel_regs->lp_count = gdb_regs[_LP_COUNT]; | ||
52 | kernel_regs->lp_end = gdb_regs[_LP_END]; | ||
53 | kernel_regs->lp_start = gdb_regs[_LP_START]; | ||
54 | kernel_regs->bta = gdb_regs[_BTA]; | ||
55 | } | ||
56 | |||
57 | |||
58 | void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs) | ||
59 | { | ||
60 | to_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *) | ||
61 | current->thread.callee_reg); | ||
62 | } | ||
63 | |||
64 | void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs) | ||
65 | { | ||
66 | from_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *) | ||
67 | current->thread.callee_reg); | ||
68 | } | ||
69 | |||
70 | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, | ||
71 | struct task_struct *task) | ||
72 | { | ||
73 | if (task) | ||
74 | to_gdb_regs(gdb_regs, task_pt_regs(task), | ||
75 | (struct callee_regs *) task->thread.callee_reg); | ||
76 | } | ||
77 | |||
78 | struct single_step_data_t { | ||
79 | uint16_t opcode[2]; | ||
80 | unsigned long address[2]; | ||
81 | int is_branch; | ||
82 | int armed; | ||
83 | } single_step_data; | ||
84 | |||
85 | static void undo_single_step(struct pt_regs *regs) | ||
86 | { | ||
87 | if (single_step_data.armed) { | ||
88 | int i; | ||
89 | |||
90 | for (i = 0; i < (single_step_data.is_branch ? 2 : 1); i++) { | ||
91 | memcpy((void *) single_step_data.address[i], | ||
92 | &single_step_data.opcode[i], | ||
93 | BREAK_INSTR_SIZE); | ||
94 | |||
95 | flush_icache_range(single_step_data.address[i], | ||
96 | single_step_data.address[i] + | ||
97 | BREAK_INSTR_SIZE); | ||
98 | } | ||
99 | single_step_data.armed = 0; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | static void place_trap(unsigned long address, void *save) | ||
104 | { | ||
105 | memcpy(save, (void *) address, BREAK_INSTR_SIZE); | ||
106 | memcpy((void *) address, &arch_kgdb_ops.gdb_bpt_instr, | ||
107 | BREAK_INSTR_SIZE); | ||
108 | flush_icache_range(address, address + BREAK_INSTR_SIZE); | ||
109 | } | ||
110 | |||
111 | static void do_single_step(struct pt_regs *regs) | ||
112 | { | ||
113 | single_step_data.is_branch = disasm_next_pc((unsigned long) | ||
114 | regs->ret, regs, (struct callee_regs *) | ||
115 | current->thread.callee_reg, | ||
116 | &single_step_data.address[0], | ||
117 | &single_step_data.address[1]); | ||
118 | |||
119 | place_trap(single_step_data.address[0], &single_step_data.opcode[0]); | ||
120 | |||
121 | if (single_step_data.is_branch) { | ||
122 | place_trap(single_step_data.address[1], | ||
123 | &single_step_data.opcode[1]); | ||
124 | } | ||
125 | |||
126 | single_step_data.armed++; | ||
127 | } | ||
128 | |||
129 | int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, | ||
130 | char *remcomInBuffer, char *remcomOutBuffer, | ||
131 | struct pt_regs *regs) | ||
132 | { | ||
133 | unsigned long addr; | ||
134 | char *ptr; | ||
135 | |||
136 | undo_single_step(regs); | ||
137 | |||
138 | switch (remcomInBuffer[0]) { | ||
139 | case 's': | ||
140 | case 'c': | ||
141 | ptr = &remcomInBuffer[1]; | ||
142 | if (kgdb_hex2long(&ptr, &addr)) | ||
143 | regs->ret = addr; | ||
144 | |||
145 | case 'D': | ||
146 | case 'k': | ||
147 | atomic_set(&kgdb_cpu_doing_single_step, -1); | ||
148 | |||
149 | if (remcomInBuffer[0] == 's') { | ||
150 | do_single_step(regs); | ||
151 | atomic_set(&kgdb_cpu_doing_single_step, | ||
152 | smp_processor_id()); | ||
153 | } | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | return -1; | ||
158 | } | ||
159 | |||
160 | unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs) | ||
161 | { | ||
162 | return instruction_pointer(regs); | ||
163 | } | ||
164 | |||
165 | int kgdb_arch_init(void) | ||
166 | { | ||
167 | single_step_data.armed = 0; | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | void kgdb_trap(struct pt_regs *regs, int param) | ||
172 | { | ||
173 | /* trap_s 3 is used for breakpoints that overwrite existing | ||
174 | * instructions, while trap_s 4 is used for compiled breakpoints. | ||
175 | * | ||
176 | * with trap_s 3 breakpoints the original instruction needs to be | ||
177 | * restored and continuation needs to start at the location of the | ||
178 | * breakpoint. | ||
179 | * | ||
180 | * with trap_s 4 (compiled) breakpoints, continuation needs to | ||
181 | * start after the breakpoint. | ||
182 | */ | ||
183 | if (param == 3) | ||
184 | instruction_pointer(regs) -= BREAK_INSTR_SIZE; | ||
185 | |||
186 | kgdb_handle_exception(1, SIGTRAP, 0, regs); | ||
187 | } | ||
188 | |||
189 | void kgdb_arch_exit(void) | ||
190 | { | ||
191 | } | ||
192 | |||
193 | void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) | ||
194 | { | ||
195 | instruction_pointer(regs) = ip; | ||
196 | } | ||
197 | |||
198 | struct kgdb_arch arch_kgdb_ops = { | ||
199 | /* breakpoint instruction: TRAP_S 0x3 */ | ||
200 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
201 | .gdb_bpt_instr = {0x78, 0x7e}, | ||
202 | #else | ||
203 | .gdb_bpt_instr = {0x7e, 0x78}, | ||
204 | #endif | ||
205 | }; | ||
diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c index ec802c52a1ca..7496995371e8 100644 --- a/arch/arc/kernel/traps.c +++ b/arch/arc/kernel/traps.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <asm/setup.h> | 20 | #include <asm/setup.h> |
21 | #include <asm/kprobes.h> | 21 | #include <asm/kprobes.h> |
22 | #include <asm/unaligned.h> | 22 | #include <asm/unaligned.h> |
23 | #include <asm/kgdb.h> | ||
23 | 24 | ||
24 | void __init trap_init(void) | 25 | void __init trap_init(void) |
25 | { | 26 | { |
@@ -141,6 +142,11 @@ void do_non_swi_trap(unsigned long cause, unsigned long address, | |||
141 | trap_is_kprobe(param, address, regs); | 142 | trap_is_kprobe(param, address, regs); |
142 | break; | 143 | break; |
143 | 144 | ||
145 | case 3: | ||
146 | case 4: | ||
147 | kgdb_trap(regs, param); | ||
148 | break; | ||
149 | |||
144 | default: | 150 | default: |
145 | break; | 151 | break; |
146 | } | 152 | } |