source: mainline/kernel/arch/mips32/src/debugger.c@ bfce7d9

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bfce7d9 was 1e1e5e1, checked in by Jakub Jermar <jakub@…>, 17 years ago

Maintain cache coherence when setting breakpoints in kconsole on mips32.

  • Property mode set to 100644
File size: 11.0 KB
Line 
1/*
2 * Copyright (c) 2005 Ondrej Palkovsky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup mips32debug
30 * @{
31 */
32/** @file
33 */
34
35#include <arch/debugger.h>
36#include <arch/barrier.h>
37#include <memstr.h>
38#include <console/kconsole.h>
39#include <console/cmd.h>
40#include <symtab.h>
41#include <print.h>
42#include <panic.h>
43#include <arch.h>
44#include <arch/cp0.h>
45#include <func.h>
46
47bpinfo_t breakpoints[BKPOINTS_MAX];
48SPINLOCK_INITIALIZE(bkpoint_lock);
49
50static int cmd_print_breakpoints(cmd_arg_t *argv);
51static cmd_info_t bkpts_info = {
52 .name = "bkpts",
53 .description = "Print breakpoint table.",
54 .func = cmd_print_breakpoints,
55 .argc = 0,
56};
57
58static int cmd_del_breakpoint(cmd_arg_t *argv);
59static cmd_arg_t del_argv = {
60 .type = ARG_TYPE_INT
61};
62static cmd_info_t delbkpt_info = {
63 .name = "delbkpt",
64 .description = "delbkpt <number> - Delete breakpoint.",
65 .func = cmd_del_breakpoint,
66 .argc = 1,
67 .argv = &del_argv
68};
69
70static int cmd_add_breakpoint(cmd_arg_t *argv);
71static cmd_arg_t add_argv = {
72 .type = ARG_TYPE_INT
73};
74static cmd_info_t addbkpt_info = {
75 .name = "addbkpt",
76 .description = "addbkpt <&symbol> - new bkpoint. Break on J/Branch "
77 "insts unsupported.",
78 .func = cmd_add_breakpoint,
79 .argc = 1,
80 .argv = &add_argv
81};
82
83static cmd_arg_t adde_argv[] = {
84 { .type = ARG_TYPE_INT },
85 { .type = ARG_TYPE_INT }
86};
87static cmd_info_t addbkpte_info = {
88 .name = "addbkpte",
89 .description = "addebkpte <&symbol> <&func> - new bkpoint. Call "
90 "func(or Nothing if 0).",
91 .func = cmd_add_breakpoint,
92 .argc = 2,
93 .argv = adde_argv
94};
95
96static struct {
97 uint32_t andmask;
98 uint32_t value;
99} jmpinstr[] = {
100 {0xf3ff0000, 0x41000000}, /* BCzF */
101 {0xf3ff0000, 0x41020000}, /* BCzFL */
102 {0xf3ff0000, 0x41010000}, /* BCzT */
103 {0xf3ff0000, 0x41030000}, /* BCzTL */
104 {0xfc000000, 0x10000000}, /* BEQ */
105 {0xfc000000, 0x50000000}, /* BEQL */
106 {0xfc1f0000, 0x04010000}, /* BEQL */
107 {0xfc1f0000, 0x04110000}, /* BGEZAL */
108 {0xfc1f0000, 0x04130000}, /* BGEZALL */
109 {0xfc1f0000, 0x04030000}, /* BGEZL */
110 {0xfc1f0000, 0x1c000000}, /* BGTZ */
111 {0xfc1f0000, 0x5c000000}, /* BGTZL */
112 {0xfc1f0000, 0x18000000}, /* BLEZ */
113 {0xfc1f0000, 0x58000000}, /* BLEZL */
114 {0xfc1f0000, 0x04000000}, /* BLTZ */
115 {0xfc1f0000, 0x04100000}, /* BLTZAL */
116 {0xfc1f0000, 0x04120000}, /* BLTZALL */
117 {0xfc1f0000, 0x04020000}, /* BLTZL */
118 {0xfc000000, 0x14000000}, /* BNE */
119 {0xfc000000, 0x54000000}, /* BNEL */
120 {0xfc000000, 0x08000000}, /* J */
121 {0xfc000000, 0x0c000000}, /* JAL */
122 {0xfc1f07ff, 0x00000009}, /* JALR */
123 {0, 0} /* EndOfTable */
124};
125
126/** Test, if the given instruction is a jump or branch instruction
127 *
128 * @param instr Instruction code
129 * @return true - it is jump instruction, false otherwise
130 */
131static bool is_jump(unative_t instr)
132{
133 int i;
134
135 for (i = 0; jmpinstr[i].andmask; i++) {
136 if ((instr & jmpinstr[i].andmask) == jmpinstr[i].value)
137 return true;
138 }
139
140 return false;
141}
142
143/** Add new breakpoint to table */
144int cmd_add_breakpoint(cmd_arg_t *argv)
145{
146 bpinfo_t *cur = NULL;
147 ipl_t ipl;
148 int i;
149
150 if (argv->intval & 0x3) {
151 printf("Not aligned instruction, forgot to use &symbol?\n");
152 return 1;
153 }
154 ipl = interrupts_disable();
155 spinlock_lock(&bkpoint_lock);
156
157 /* Check, that the breakpoints do not conflict */
158 for (i = 0; i < BKPOINTS_MAX; i++) {
159 if (breakpoints[i].address == (uintptr_t)argv->intval) {
160 printf("Duplicate breakpoint %d.\n", i);
161 spinlock_unlock(&bkpoints_lock);
162 return 0;
163 } else if (breakpoints[i].address == (uintptr_t)argv->intval +
164 sizeof(unative_t) || breakpoints[i].address ==
165 (uintptr_t)argv->intval - sizeof(unative_t)) {
166 printf("Adjacent breakpoints not supported, conflict "
167 "with %d.\n", i);
168 spinlock_unlock(&bkpoints_lock);
169 return 0;
170 }
171
172 }
173
174 for (i = 0; i < BKPOINTS_MAX; i++)
175 if (!breakpoints[i].address) {
176 cur = &breakpoints[i];
177 break;
178 }
179 if (!cur) {
180 printf("Too many breakpoints.\n");
181 spinlock_unlock(&bkpoint_lock);
182 interrupts_restore(ipl);
183 return 0;
184 }
185 cur->address = (uintptr_t) argv->intval;
186 printf("Adding breakpoint on address: %p\n", argv->intval);
187 cur->instruction = ((unative_t *)cur->address)[0];
188 cur->nextinstruction = ((unative_t *)cur->address)[1];
189 if (argv == &add_argv) {
190 cur->flags = 0;
191 } else { /* We are add extended */
192 cur->flags = BKPOINT_FUNCCALL;
193 cur->bkfunc = (void (*)(void *, istate_t *)) argv[1].intval;
194 }
195 if (is_jump(cur->instruction))
196 cur->flags |= BKPOINT_ONESHOT;
197 cur->counter = 0;
198
199 /* Set breakpoint */
200 *((unative_t *)cur->address) = 0x0d;
201 smc_coherence(cur->address);
202
203 spinlock_unlock(&bkpoint_lock);
204 interrupts_restore(ipl);
205
206 return 1;
207}
208
209/** Remove breakpoint from table */
210int cmd_del_breakpoint(cmd_arg_t *argv)
211{
212 bpinfo_t *cur;
213 ipl_t ipl;
214
215 if (argv->intval > BKPOINTS_MAX) {
216 printf("Invalid breakpoint number.\n");
217 return 0;
218 }
219 ipl = interrupts_disable();
220 spinlock_lock(&bkpoint_lock);
221
222 cur = &breakpoints[argv->intval];
223 if (!cur->address) {
224 printf("Breakpoint does not exist.\n");
225 spinlock_unlock(&bkpoint_lock);
226 interrupts_restore(ipl);
227 return 0;
228 }
229 if ((cur->flags & BKPOINT_INPROG) && (cur->flags & BKPOINT_ONESHOT)) {
230 printf("Cannot remove one-shot breakpoint in-progress\n");
231 spinlock_unlock(&bkpoint_lock);
232 interrupts_restore(ipl);
233 return 0;
234 }
235 ((uint32_t *)cur->address)[0] = cur->instruction;
236 smc_coherence(((uint32_t *)cur->address)[0]);
237 ((uint32_t *)cur->address)[1] = cur->nextinstruction;
238 smc_coherence(((uint32_t *)cur->address)[1]);
239
240 cur->address = NULL;
241
242 spinlock_unlock(&bkpoint_lock);
243 interrupts_restore(ipl);
244 return 1;
245}
246
247/** Print table of active breakpoints */
248int cmd_print_breakpoints(cmd_arg_t *argv)
249{
250 unsigned int i;
251 char *symbol;
252
253 printf("# Count Address INPROG ONESHOT FUNCCALL In symbol\n");
254 printf("-- ----- ---------- ------ ------- -------- ---------\n");
255
256 for (i = 0; i < BKPOINTS_MAX; i++)
257 if (breakpoints[i].address) {
258 symbol = get_symtab_entry(breakpoints[i].address);
259
260 printf("%-2u %-5d %#10zx %-6s %-7s %-8s %s\n", i,
261 breakpoints[i].counter, breakpoints[i].address,
262 ((breakpoints[i].flags & BKPOINT_INPROG) ? "true" :
263 "false"), ((breakpoints[i].flags & BKPOINT_ONESHOT)
264 ? "true" : "false"), ((breakpoints[i].flags &
265 BKPOINT_FUNCCALL) ? "true" : "false"), symbol);
266 }
267 return 1;
268}
269
270/** Initialize debugger */
271void debugger_init()
272{
273 int i;
274
275 for (i = 0; i < BKPOINTS_MAX; i++)
276 breakpoints[i].address = NULL;
277
278 cmd_initialize(&bkpts_info);
279 if (!cmd_register(&bkpts_info))
280 panic("could not register command %s\n", bkpts_info.name);
281
282 cmd_initialize(&delbkpt_info);
283 if (!cmd_register(&delbkpt_info))
284 panic("could not register command %s\n", delbkpt_info.name);
285
286 cmd_initialize(&addbkpt_info);
287 if (!cmd_register(&addbkpt_info))
288 panic("could not register command %s\n", addbkpt_info.name);
289
290 cmd_initialize(&addbkpte_info);
291 if (!cmd_register(&addbkpte_info))
292 panic("could not register command %s\n", addbkpte_info.name);
293}
294
295/** Handle breakpoint
296 *
297 * Find breakpoint in breakpoint table.
298 * If found, call kconsole, set break on next instruction and reexecute.
299 * If we are on "next instruction", set it back on the first and reexecute.
300 * If breakpoint not found in breakpoint table, call kconsole and start
301 * next instruction.
302 */
303void debugger_bpoint(istate_t *istate)
304{
305 bpinfo_t *cur = NULL;
306 uintptr_t fireaddr = istate->epc;
307 int i;
308
309 /* test branch delay slot */
310 if (cp0_cause_read() & 0x80000000)
311 panic("Breakpoint in branch delay slot not supported.\n");
312
313 spinlock_lock(&bkpoint_lock);
314 for (i = 0; i < BKPOINTS_MAX; i++) {
315 /* Normal breakpoint */
316 if (fireaddr == breakpoints[i].address &&
317 !(breakpoints[i].flags & BKPOINT_REINST)) {
318 cur = &breakpoints[i];
319 break;
320 }
321 /* Reinst only breakpoint */
322 if ((breakpoints[i].flags & BKPOINT_REINST) &&
323 (fireaddr == breakpoints[i].address + sizeof(unative_t))) {
324 cur = &breakpoints[i];
325 break;
326 }
327 }
328 if (cur) {
329 if (cur->flags & BKPOINT_REINST) {
330 /* Set breakpoint on first instruction */
331 ((uint32_t *)cur->address)[0] = 0x0d;
332 smc_coherence(((uint32_t *)cur->address)[0]);
333 /* Return back the second */
334 ((uint32_t *)cur->address)[1] = cur->nextinstruction;
335 smc_coherence(((uint32_t *)cur->address)[1]);
336 cur->flags &= ~BKPOINT_REINST;
337 spinlock_unlock(&bkpoint_lock);
338 return;
339 }
340 if (cur->flags & BKPOINT_INPROG)
341 printf("Warning: breakpoint recursion\n");
342
343 if (!(cur->flags & BKPOINT_FUNCCALL))
344 printf("***Breakpoint %d: %p in %s.\n", i, fireaddr,
345 get_symtab_entry(istate->epc));
346
347 /* Return first instruction back */
348 ((uint32_t *)cur->address)[0] = cur->instruction;
349 smc_coherence(cur->address);
350
351 if (! (cur->flags & BKPOINT_ONESHOT)) {
352 /* Set Breakpoint on next instruction */
353 ((uint32_t *)cur->address)[1] = 0x0d;
354 cur->flags |= BKPOINT_REINST;
355 }
356 cur->flags |= BKPOINT_INPROG;
357 } else {
358 printf("***Breakpoint %p in %s.\n", fireaddr,
359 get_symtab_entry(fireaddr));
360 /* Move on to next instruction */
361 istate->epc += 4;
362 }
363 if (cur)
364 cur->counter++;
365 if (cur && (cur->flags & BKPOINT_FUNCCALL)) {
366 /* Allow zero bkfunc, just for counting */
367 if (cur->bkfunc)
368 cur->bkfunc(cur, istate);
369 } else {
370 printf("***Type 'exit' to exit kconsole.\n");
371 /* This disables all other processors - we are not SMP,
372 * actually this gets us to cpu_halt, if scheduler() is run
373 * - we generally do not want scheduler to be run from debug,
374 * so this is a good idea
375 */
376 atomic_set(&haltstate,1);
377 spinlock_unlock(&bkpoint_lock);
378
379 kconsole("debug");
380
381 spinlock_lock(&bkpoint_lock);
382 atomic_set(&haltstate,0);
383 }
384 if (cur && cur->address == fireaddr && (cur->flags & BKPOINT_INPROG)) {
385 /* Remove one-shot breakpoint */
386 if ((cur->flags & BKPOINT_ONESHOT))
387 cur->address = NULL;
388 /* Remove in-progress flag */
389 cur->flags &= ~BKPOINT_INPROG;
390 }
391 spinlock_unlock(&bkpoint_lock);
392}
393
394/** @}
395 */
Note: See TracBrowser for help on using the repository browser.