diff options
author | Ley Foon Tan <lftan@altera.com> | 2015-02-16 06:26:43 -0500 |
---|---|---|
committer | Ley Foon Tan <lftan@altera.com> | 2015-02-16 06:26:43 -0500 |
commit | d16d2be111a61baf3a4696f07bfc7a8e36697cec (patch) | |
tree | 88284b55d67971ccdaa69fabe0b406098b263644 /arch/nios2/kernel | |
parent | 08c283cc039efc2cf7d64d7e69d8d01de825fc6c (diff) |
nios2: add kgdb support
Signed-off-by: Ley Foon Tan <lftan@altera.com>
Diffstat (limited to 'arch/nios2/kernel')
-rw-r--r-- | arch/nios2/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/nios2/kernel/entry.S | 12 | ||||
-rw-r--r-- | arch/nios2/kernel/kgdb.c | 171 |
3 files changed, 183 insertions, 1 deletions
diff --git a/arch/nios2/kernel/Makefile b/arch/nios2/kernel/Makefile index eaaa894c432f..1aae25703657 100644 --- a/arch/nios2/kernel/Makefile +++ b/arch/nios2/kernel/Makefile | |||
@@ -21,5 +21,6 @@ obj-y += time.o | |||
21 | obj-y += traps.o | 21 | obj-y += traps.o |
22 | 22 | ||
23 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | 23 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o |
24 | obj-$(CONFIG_KGDB) += kgdb.o | ||
24 | obj-$(CONFIG_MODULES) += module.o | 25 | obj-$(CONFIG_MODULES) += module.o |
25 | obj-$(CONFIG_NIOS2_ALIGNMENT_TRAP) += misaligned.o | 26 | obj-$(CONFIG_NIOS2_ALIGNMENT_TRAP) += misaligned.o |
diff --git a/arch/nios2/kernel/entry.S b/arch/nios2/kernel/entry.S index 0bdfd13ff98b..7729bd3f2e79 100644 --- a/arch/nios2/kernel/entry.S +++ b/arch/nios2/kernel/entry.S | |||
@@ -121,7 +121,11 @@ trap_table: | |||
121 | .word instruction_trap /* 27 */ | 121 | .word instruction_trap /* 27 */ |
122 | .word instruction_trap /* 28 */ | 122 | .word instruction_trap /* 28 */ |
123 | .word instruction_trap /* 29 */ | 123 | .word instruction_trap /* 29 */ |
124 | .word instruction_trap /* 30 */ | 124 | #ifdef CONFIG_KGDB |
125 | .word handle_kgdb_breakpoint /* 30 KGDB breakpoint */ | ||
126 | #else | ||
127 | .word instruction_trap /* 30 */ | ||
128 | #endif | ||
125 | .word handle_breakpoint /* 31 */ | 129 | .word handle_breakpoint /* 31 */ |
126 | 130 | ||
127 | .text | 131 | .text |
@@ -445,6 +449,12 @@ handle_diverror: | |||
445 | call handle_diverror_c | 449 | call handle_diverror_c |
446 | br ret_from_exception | 450 | br ret_from_exception |
447 | 451 | ||
452 | #ifdef CONFIG_KGDB | ||
453 | handle_kgdb_breakpoint: | ||
454 | call kgdb_breakpoint_c | ||
455 | br ret_from_exception | ||
456 | #endif | ||
457 | |||
448 | /* | 458 | /* |
449 | * Beware - when entering resume, prev (the current task) is | 459 | * Beware - when entering resume, prev (the current task) is |
450 | * in r4, next (the new task) is in r5, don't change these | 460 | * in r4, next (the new task) is in r5, don't change these |
diff --git a/arch/nios2/kernel/kgdb.c b/arch/nios2/kernel/kgdb.c new file mode 100644 index 000000000000..117859122d1c --- /dev/null +++ b/arch/nios2/kernel/kgdb.c | |||
@@ -0,0 +1,171 @@ | |||
1 | /* | ||
2 | * Nios2 KGDB support | ||
3 | * | ||
4 | * Copyright (C) 2015 Altera Corporation | ||
5 | * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> | ||
6 | * | ||
7 | * Based on the code posted by Kazuyasu on the Altera Forum at: | ||
8 | * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20 | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
22 | * | ||
23 | */ | ||
24 | #include <linux/ptrace.h> | ||
25 | #include <linux/kgdb.h> | ||
26 | #include <linux/kdebug.h> | ||
27 | #include <linux/io.h> | ||
28 | |||
29 | static int wait_for_remote_debugger; | ||
30 | |||
31 | struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = | ||
32 | { | ||
33 | { "zero", GDB_SIZEOF_REG, -1 }, | ||
34 | { "at", GDB_SIZEOF_REG, offsetof(struct pt_regs, r1) }, | ||
35 | { "r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, r2) }, | ||
36 | { "r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, r3) }, | ||
37 | { "r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, r4) }, | ||
38 | { "r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, r5) }, | ||
39 | { "r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, r6) }, | ||
40 | { "r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, r7) }, | ||
41 | { "r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, r8) }, | ||
42 | { "r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, r9) }, | ||
43 | { "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, r10) }, | ||
44 | { "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, r11) }, | ||
45 | { "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, r12) }, | ||
46 | { "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, r13) }, | ||
47 | { "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, r14) }, | ||
48 | { "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, r15) }, | ||
49 | { "r16", GDB_SIZEOF_REG, -1 }, | ||
50 | { "r17", GDB_SIZEOF_REG, -1 }, | ||
51 | { "r18", GDB_SIZEOF_REG, -1 }, | ||
52 | { "r19", GDB_SIZEOF_REG, -1 }, | ||
53 | { "r20", GDB_SIZEOF_REG, -1 }, | ||
54 | { "r21", GDB_SIZEOF_REG, -1 }, | ||
55 | { "r22", GDB_SIZEOF_REG, -1 }, | ||
56 | { "r23", GDB_SIZEOF_REG, -1 }, | ||
57 | { "et", GDB_SIZEOF_REG, -1 }, | ||
58 | { "bt", GDB_SIZEOF_REG, -1 }, | ||
59 | { "gp", GDB_SIZEOF_REG, offsetof(struct pt_regs, gp) }, | ||
60 | { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, sp) }, | ||
61 | { "fp", GDB_SIZEOF_REG, offsetof(struct pt_regs, fp) }, | ||
62 | { "ea", GDB_SIZEOF_REG, -1 }, | ||
63 | { "ba", GDB_SIZEOF_REG, -1 }, | ||
64 | { "ra", GDB_SIZEOF_REG, offsetof(struct pt_regs, ra) }, | ||
65 | { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, ea) }, | ||
66 | { "status", GDB_SIZEOF_REG, -1 }, | ||
67 | { "estatus", GDB_SIZEOF_REG, offsetof(struct pt_regs, estatus) }, | ||
68 | { "bstatus", GDB_SIZEOF_REG, -1 }, | ||
69 | { "ienable", GDB_SIZEOF_REG, -1 }, | ||
70 | { "ipending", GDB_SIZEOF_REG, -1}, | ||
71 | { "cpuid", GDB_SIZEOF_REG, -1 }, | ||
72 | { "ctl6", GDB_SIZEOF_REG, -1 }, | ||
73 | { "exception", GDB_SIZEOF_REG, -1 }, | ||
74 | { "pteaddr", GDB_SIZEOF_REG, -1 }, | ||
75 | { "tlbacc", GDB_SIZEOF_REG, -1 }, | ||
76 | { "tlbmisc", GDB_SIZEOF_REG, -1 }, | ||
77 | { "eccinj", GDB_SIZEOF_REG, -1 }, | ||
78 | { "badaddr", GDB_SIZEOF_REG, -1 }, | ||
79 | { "config", GDB_SIZEOF_REG, -1 }, | ||
80 | { "mpubase", GDB_SIZEOF_REG, -1 }, | ||
81 | { "mpuacc", GDB_SIZEOF_REG, -1 }, | ||
82 | }; | ||
83 | |||
84 | char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) | ||
85 | { | ||
86 | if (regno >= DBG_MAX_REG_NUM || regno < 0) | ||
87 | return NULL; | ||
88 | |||
89 | if (dbg_reg_def[regno].offset != -1) | ||
90 | memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, | ||
91 | dbg_reg_def[regno].size); | ||
92 | else | ||
93 | memset(mem, 0, dbg_reg_def[regno].size); | ||
94 | |||
95 | return dbg_reg_def[regno].name; | ||
96 | } | ||
97 | |||
98 | int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) | ||
99 | { | ||
100 | if (regno >= DBG_MAX_REG_NUM || regno < 0) | ||
101 | return -EINVAL; | ||
102 | |||
103 | if (dbg_reg_def[regno].offset != -1) | ||
104 | memcpy((void *)regs + dbg_reg_def[regno].offset, mem, | ||
105 | dbg_reg_def[regno].size); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) | ||
111 | { | ||
112 | memset((char *)gdb_regs, 0, NUMREGBYTES); | ||
113 | gdb_regs[GDB_SP] = p->thread.kregs->sp; | ||
114 | gdb_regs[GDB_PC] = p->thread.kregs->ea; | ||
115 | } | ||
116 | |||
117 | void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) | ||
118 | { | ||
119 | regs->ea = pc; | ||
120 | } | ||
121 | |||
122 | int kgdb_arch_handle_exception(int vector, int signo, int err_code, | ||
123 | char *remcom_in_buffer, char *remcom_out_buffer, | ||
124 | struct pt_regs *regs) | ||
125 | { | ||
126 | char *ptr; | ||
127 | unsigned long addr; | ||
128 | |||
129 | switch (remcom_in_buffer[0]) { | ||
130 | case 's': | ||
131 | case 'c': | ||
132 | /* handle the optional parameters */ | ||
133 | ptr = &remcom_in_buffer[1]; | ||
134 | if (kgdb_hex2long(&ptr, &addr)) | ||
135 | regs->ea = addr; | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | return -1; /* this means that we do not want to exit from the handler */ | ||
141 | } | ||
142 | |||
143 | asmlinkage void kgdb_breakpoint_c(struct pt_regs *regs) | ||
144 | { | ||
145 | /* | ||
146 | * The breakpoint entry code has moved the PC on by 4 bytes, so we must | ||
147 | * move it back. This could be done on the host but we do it here | ||
148 | */ | ||
149 | if (!wait_for_remote_debugger) | ||
150 | regs->ea -= 4; | ||
151 | else /* pass the first trap 30 code */ | ||
152 | wait_for_remote_debugger = 0; | ||
153 | |||
154 | kgdb_handle_exception(30, SIGTRAP, 0, regs); | ||
155 | } | ||
156 | |||
157 | int kgdb_arch_init(void) | ||
158 | { | ||
159 | wait_for_remote_debugger = 1; | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | void kgdb_arch_exit(void) | ||
164 | { | ||
165 | /* Nothing to do */ | ||
166 | } | ||
167 | |||
168 | struct kgdb_arch arch_kgdb_ops = { | ||
169 | /* Breakpoint instruction: trap 30 */ | ||
170 | .gdb_bpt_instr = { 0xba, 0x6f, 0x3b, 0x00 }, | ||
171 | }; | ||