aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64
diff options
context:
space:
mode:
authorVijaya Kumar K <Vijaya.Kumar@caviumnetworks.com>2014-01-28 06:20:18 -0500
committerCatalin Marinas <catalin.marinas@arm.com>2014-02-26 06:16:25 -0500
commitbcf5763b0d58d20e288ac52f96cbd7788e262cac (patch)
tree2f3ad53a9915631858dbc4991165344b8a22a2c3 /arch/arm64
parentc7db4ff5d2b459a579d348532a92fd5885520ce6 (diff)
arm64: KGDB: Add Basic KGDB support
Add KGDB debug support for kernel debugging. With this patch, basic KGDB debugging is possible.GDB register layout is updated and GDB tool can establish connection with target and can set/clear breakpoints. Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@caviumnetworks.com> Reviewed-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'arch/arm64')
-rw-r--r--arch/arm64/include/asm/debug-monitors.h47
-rw-r--r--arch/arm64/include/asm/kgdb.h84
-rw-r--r--arch/arm64/kernel/Makefile1
-rw-r--r--arch/arm64/kernel/kgdb.c288
4 files changed, 420 insertions, 0 deletions
diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h
index ee9f28e245aa..6e9b5b36921c 100644
--- a/arch/arm64/include/asm/debug-monitors.h
+++ b/arch/arm64/include/asm/debug-monitors.h
@@ -26,6 +26,53 @@
26#define DBG_ESR_EVT_HWWP 0x2 26#define DBG_ESR_EVT_HWWP 0x2
27#define DBG_ESR_EVT_BRK 0x6 27#define DBG_ESR_EVT_BRK 0x6
28 28
29/*
30 * Break point instruction encoding
31 */
32#define BREAK_INSTR_SIZE 4
33
34/*
35 * ESR values expected for dynamic and compile time BRK instruction
36 */
37#define DBG_ESR_VAL_BRK(x) (0xf2000000 | ((x) & 0xfffff))
38
39/*
40 * #imm16 values used for BRK instruction generation
41 * Allowed values for kgbd are 0x400 - 0x7ff
42 * 0x400: for dynamic BRK instruction
43 * 0x401: for compile time BRK instruction
44 */
45#define KGDB_DYN_DGB_BRK_IMM 0x400
46#define KDBG_COMPILED_DBG_BRK_IMM 0x401
47
48/*
49 * BRK instruction encoding
50 * The #imm16 value should be placed at bits[20:5] within BRK ins
51 */
52#define AARCH64_BREAK_MON 0xd4200000
53
54/*
55 * Extract byte from BRK instruction
56 */
57#define KGDB_DYN_DGB_BRK_INS_BYTE(x) \
58 ((((AARCH64_BREAK_MON) & 0xffe0001f) >> (x * 8)) & 0xff)
59
60/*
61 * Extract byte from BRK #imm16
62 */
63#define KGBD_DYN_DGB_BRK_IMM_BYTE(x) \
64 (((((KGDB_DYN_DGB_BRK_IMM) & 0xffff) << 5) >> (x * 8)) & 0xff)
65
66#define KGDB_DYN_DGB_BRK_BYTE(x) \
67 (KGDB_DYN_DGB_BRK_INS_BYTE(x) | KGBD_DYN_DGB_BRK_IMM_BYTE(x))
68
69#define KGDB_DYN_BRK_INS_BYTE0 KGDB_DYN_DGB_BRK_BYTE(0)
70#define KGDB_DYN_BRK_INS_BYTE1 KGDB_DYN_DGB_BRK_BYTE(1)
71#define KGDB_DYN_BRK_INS_BYTE2 KGDB_DYN_DGB_BRK_BYTE(2)
72#define KGDB_DYN_BRK_INS_BYTE3 KGDB_DYN_DGB_BRK_BYTE(3)
73
74#define CACHE_FLUSH_IS_SAFE 1
75
29enum debug_el { 76enum debug_el {
30 DBG_ACTIVE_EL0 = 0, 77 DBG_ACTIVE_EL0 = 0,
31 DBG_ACTIVE_EL1, 78 DBG_ACTIVE_EL1,
diff --git a/arch/arm64/include/asm/kgdb.h b/arch/arm64/include/asm/kgdb.h
new file mode 100644
index 000000000000..3c8aafc1082f
--- /dev/null
+++ b/arch/arm64/include/asm/kgdb.h
@@ -0,0 +1,84 @@
1/*
2 * AArch64 KGDB support
3 *
4 * Based on arch/arm/include/kgdb.h
5 *
6 * Copyright (C) 2013 Cavium Inc.
7 * Author: Vijaya Kumar K <vijaya.kumar@caviumnetworks.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#ifndef __ARM_KGDB_H
23#define __ARM_KGDB_H
24
25#include <linux/ptrace.h>
26#include <asm/debug-monitors.h>
27
28#ifndef __ASSEMBLY__
29
30static inline void arch_kgdb_breakpoint(void)
31{
32 asm ("brk %0" : : "I" (KDBG_COMPILED_DBG_BRK_IMM));
33}
34
35extern void kgdb_handle_bus_error(void);
36extern int kgdb_fault_expected;
37
38#endif /* !__ASSEMBLY__ */
39
40/*
41 * gdb is expecting the following registers layout.
42 *
43 * General purpose regs:
44 * r0-r30: 64 bit
45 * sp,pc : 64 bit
46 * pstate : 64 bit
47 * Total: 34
48 * FPU regs:
49 * f0-f31: 128 bit
50 * Total: 32
51 * Extra regs
52 * fpsr & fpcr: 32 bit
53 * Total: 2
54 *
55 */
56
57#define _GP_REGS 34
58#define _FP_REGS 32
59#define _EXTRA_REGS 2
60/*
61 * general purpose registers size in bytes.
62 * pstate is only 4 bytes. subtract 4 bytes
63 */
64#define GP_REG_BYTES (_GP_REGS * 8)
65#define DBG_MAX_REG_NUM (_GP_REGS + _FP_REGS + _EXTRA_REGS)
66
67/*
68 * Size of I/O buffer for gdb packet.
69 * considering to hold all register contents, size is set
70 */
71
72#define BUFMAX 2048
73
74/*
75 * Number of bytes required for gdb_regs buffer.
76 * _GP_REGS: 8 bytes, _FP_REGS: 16 bytes and _EXTRA_REGS: 4 bytes each
77 * GDB fails to connect for size beyond this with error
78 * "'g' packet reply is too long"
79 */
80
81#define NUMREGBYTES ((_GP_REGS * 8) + (_FP_REGS * 16) + \
82 (_EXTRA_REGS * 4))
83
84#endif /* __ASM_KGDB_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 2d4554b13410..e52bcdc6f097 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -20,6 +20,7 @@ arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
20arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o 20arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
21arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o 21arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
22arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o 22arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
23arm64-obj-$(CONFIG_KGDB) += kgdb.o
23 24
24obj-y += $(arm64-obj-y) vdso/ 25obj-y += $(arm64-obj-y) vdso/
25obj-m += $(arm64-obj-m) 26obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c
new file mode 100644
index 000000000000..4b7a5695175e
--- /dev/null
+++ b/arch/arm64/kernel/kgdb.c
@@ -0,0 +1,288 @@
1/*
2 * AArch64 KGDB support
3 *
4 * Based on arch/arm/kernel/kgdb.c
5 *
6 * Copyright (C) 2013 Cavium Inc.
7 * Author: Vijaya Kumar K <vijaya.kumar@caviumnetworks.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <linux/irq.h>
23#include <linux/kdebug.h>
24#include <linux/kgdb.h>
25#include <asm/traps.h>
26
27struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
28 { "x0", 8, offsetof(struct pt_regs, regs[0])},
29 { "x1", 8, offsetof(struct pt_regs, regs[1])},
30 { "x2", 8, offsetof(struct pt_regs, regs[2])},
31 { "x3", 8, offsetof(struct pt_regs, regs[3])},
32 { "x4", 8, offsetof(struct pt_regs, regs[4])},
33 { "x5", 8, offsetof(struct pt_regs, regs[5])},
34 { "x6", 8, offsetof(struct pt_regs, regs[6])},
35 { "x7", 8, offsetof(struct pt_regs, regs[7])},
36 { "x8", 8, offsetof(struct pt_regs, regs[8])},
37 { "x9", 8, offsetof(struct pt_regs, regs[9])},
38 { "x10", 8, offsetof(struct pt_regs, regs[10])},
39 { "x11", 8, offsetof(struct pt_regs, regs[11])},
40 { "x12", 8, offsetof(struct pt_regs, regs[12])},
41 { "x13", 8, offsetof(struct pt_regs, regs[13])},
42 { "x14", 8, offsetof(struct pt_regs, regs[14])},
43 { "x15", 8, offsetof(struct pt_regs, regs[15])},
44 { "x16", 8, offsetof(struct pt_regs, regs[16])},
45 { "x17", 8, offsetof(struct pt_regs, regs[17])},
46 { "x18", 8, offsetof(struct pt_regs, regs[18])},
47 { "x19", 8, offsetof(struct pt_regs, regs[19])},
48 { "x20", 8, offsetof(struct pt_regs, regs[20])},
49 { "x21", 8, offsetof(struct pt_regs, regs[21])},
50 { "x22", 8, offsetof(struct pt_regs, regs[22])},
51 { "x23", 8, offsetof(struct pt_regs, regs[23])},
52 { "x24", 8, offsetof(struct pt_regs, regs[24])},
53 { "x25", 8, offsetof(struct pt_regs, regs[25])},
54 { "x26", 8, offsetof(struct pt_regs, regs[26])},
55 { "x27", 8, offsetof(struct pt_regs, regs[27])},
56 { "x28", 8, offsetof(struct pt_regs, regs[28])},
57 { "x29", 8, offsetof(struct pt_regs, regs[29])},
58 { "x30", 8, offsetof(struct pt_regs, regs[30])},
59 { "sp", 8, offsetof(struct pt_regs, sp)},
60 { "pc", 8, offsetof(struct pt_regs, pc)},
61 { "pstate", 8, offsetof(struct pt_regs, pstate)},
62 { "v0", 16, -1 },
63 { "v1", 16, -1 },
64 { "v2", 16, -1 },
65 { "v3", 16, -1 },
66 { "v4", 16, -1 },
67 { "v5", 16, -1 },
68 { "v6", 16, -1 },
69 { "v7", 16, -1 },
70 { "v8", 16, -1 },
71 { "v9", 16, -1 },
72 { "v10", 16, -1 },
73 { "v11", 16, -1 },
74 { "v12", 16, -1 },
75 { "v13", 16, -1 },
76 { "v14", 16, -1 },
77 { "v15", 16, -1 },
78 { "v16", 16, -1 },
79 { "v17", 16, -1 },
80 { "v18", 16, -1 },
81 { "v19", 16, -1 },
82 { "v20", 16, -1 },
83 { "v21", 16, -1 },
84 { "v22", 16, -1 },
85 { "v23", 16, -1 },
86 { "v24", 16, -1 },
87 { "v25", 16, -1 },
88 { "v26", 16, -1 },
89 { "v27", 16, -1 },
90 { "v28", 16, -1 },
91 { "v29", 16, -1 },
92 { "v30", 16, -1 },
93 { "v31", 16, -1 },
94 { "fpsr", 4, -1 },
95 { "fpcr", 4, -1 },
96};
97
98char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
99{
100 if (regno >= DBG_MAX_REG_NUM || regno < 0)
101 return NULL;
102
103 if (dbg_reg_def[regno].offset != -1)
104 memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
105 dbg_reg_def[regno].size);
106 else
107 memset(mem, 0, dbg_reg_def[regno].size);
108 return dbg_reg_def[regno].name;
109}
110
111int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
112{
113 if (regno >= DBG_MAX_REG_NUM || regno < 0)
114 return -EINVAL;
115
116 if (dbg_reg_def[regno].offset != -1)
117 memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
118 dbg_reg_def[regno].size);
119 return 0;
120}
121
122void
123sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
124{
125 struct pt_regs *thread_regs;
126
127 /* Initialize to zero */
128 memset((char *)gdb_regs, 0, NUMREGBYTES);
129 thread_regs = task_pt_regs(task);
130 memcpy((void *)gdb_regs, (void *)thread_regs->regs, GP_REG_BYTES);
131}
132
133void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
134{
135 regs->pc = pc;
136}
137
138static int compiled_break;
139
140int kgdb_arch_handle_exception(int exception_vector, int signo,
141 int err_code, char *remcom_in_buffer,
142 char *remcom_out_buffer,
143 struct pt_regs *linux_regs)
144{
145 unsigned long addr;
146 char *ptr;
147 int err;
148
149 switch (remcom_in_buffer[0]) {
150 case 'D':
151 case 'k':
152 /*
153 * Packet D (Detach), k (kill). No special handling
154 * is required here. Handle same as c packet.
155 */
156 case 'c':
157 /*
158 * Packet c (Continue) to continue executing.
159 * Set pc to required address.
160 * Try to read optional parameter and set pc.
161 * If this was a compiled breakpoint, we need to move
162 * to the next instruction else we will just breakpoint
163 * over and over again.
164 */
165 ptr = &remcom_in_buffer[1];
166 if (kgdb_hex2long(&ptr, &addr))
167 kgdb_arch_set_pc(linux_regs, addr);
168 else if (compiled_break == 1)
169 kgdb_arch_set_pc(linux_regs, linux_regs->pc + 4);
170
171 compiled_break = 0;
172 err = 0;
173 break;
174 default:
175 err = -1;
176 }
177 return err;
178}
179
180static int kgdb_brk_fn(struct pt_regs *regs, unsigned int esr)
181{
182 kgdb_handle_exception(1, SIGTRAP, 0, regs);
183 return 0;
184}
185
186static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr)
187{
188 compiled_break = 1;
189 kgdb_handle_exception(1, SIGTRAP, 0, regs);
190
191 return 0;
192}
193
194static struct break_hook kgdb_brkpt_hook = {
195 .esr_mask = 0xffffffff,
196 .esr_val = DBG_ESR_VAL_BRK(KGDB_DYN_DGB_BRK_IMM),
197 .fn = kgdb_brk_fn
198};
199
200static struct break_hook kgdb_compiled_brkpt_hook = {
201 .esr_mask = 0xffffffff,
202 .esr_val = DBG_ESR_VAL_BRK(KDBG_COMPILED_DBG_BRK_IMM),
203 .fn = kgdb_compiled_brk_fn
204};
205
206static void kgdb_call_nmi_hook(void *ignored)
207{
208 kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs());
209}
210
211void kgdb_roundup_cpus(unsigned long flags)
212{
213 local_irq_enable();
214 smp_call_function(kgdb_call_nmi_hook, NULL, 0);
215 local_irq_disable();
216}
217
218static int __kgdb_notify(struct die_args *args, unsigned long cmd)
219{
220 struct pt_regs *regs = args->regs;
221
222 if (kgdb_handle_exception(1, args->signr, cmd, regs))
223 return NOTIFY_DONE;
224 return NOTIFY_STOP;
225}
226
227static int
228kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
229{
230 unsigned long flags;
231 int ret;
232
233 local_irq_save(flags);
234 ret = __kgdb_notify(ptr, cmd);
235 local_irq_restore(flags);
236
237 return ret;
238}
239
240static struct notifier_block kgdb_notifier = {
241 .notifier_call = kgdb_notify,
242 /*
243 * Want to be lowest priority
244 */
245 .priority = -INT_MAX,
246};
247
248/*
249 * kgdb_arch_init - Perform any architecture specific initalization.
250 * This function will handle the initalization of any architecture
251 * specific callbacks.
252 */
253int kgdb_arch_init(void)
254{
255 int ret = register_die_notifier(&kgdb_notifier);
256
257 if (ret != 0)
258 return ret;
259
260 register_break_hook(&kgdb_brkpt_hook);
261 register_break_hook(&kgdb_compiled_brkpt_hook);
262 return 0;
263}
264
265/*
266 * kgdb_arch_exit - Perform any architecture specific uninitalization.
267 * This function will handle the uninitalization of any architecture
268 * specific callbacks, for dynamic registration and unregistration.
269 */
270void kgdb_arch_exit(void)
271{
272 unregister_break_hook(&kgdb_brkpt_hook);
273 unregister_break_hook(&kgdb_compiled_brkpt_hook);
274 unregister_die_notifier(&kgdb_notifier);
275}
276
277/*
278 * ARM instructions are always in LE.
279 * Break instruction is encoded in LE format
280 */
281struct kgdb_arch arch_kgdb_ops = {
282 .gdb_bpt_instr = {
283 KGDB_DYN_BRK_INS_BYTE0,
284 KGDB_DYN_BRK_INS_BYTE1,
285 KGDB_DYN_BRK_INS_BYTE2,
286 KGDB_DYN_BRK_INS_BYTE3,
287 }
288};