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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fc10e1b was e3306d04, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Convert atomic_t to atomic_size_t (4): Use atomic_store instead of atomic_set

  • Property mode set to 100644
File size: 11.2 KB
RevLine 
[5bb8e45]1/*
[df4ed85]2 * Copyright (c) 2005 Ondrej Palkovsky
[5bb8e45]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
[06e1e95]29/** @addtogroup mips32debug
[b45c443]30 * @{
31 */
32/** @file
33 */
34
[5bb8e45]35#include <arch/debugger.h>
[05882233]36#include <barrier.h>
[5bb8e45]37#include <console/kconsole.h>
38#include <console/cmd.h>
39#include <print.h>
[b2fa1204]40#include <log.h>
[5bb8e45]41#include <panic.h>
42#include <arch.h>
43#include <arch/cp0.h>
[b2e121a]44#include <halt.h>
[e2b762ec]45#include <symtab.h>
46
[5bb8e45]47bpinfo_t breakpoints[BKPOINTS_MAX];
[da1bafb]48IRQ_SPINLOCK_STATIC_INITIALIZE(bkpoint_lock);
[5bb8e45]49
[76fca31]50#ifdef CONFIG_KCONSOLE
51
[da1bafb]52static int cmd_print_breakpoints(cmd_arg_t *);
53static int cmd_del_breakpoint(cmd_arg_t *);
54static int cmd_add_breakpoint(cmd_arg_t *);
55
[0132630]56static cmd_info_t bkpts_info = {
57 .name = "bkpts",
[5bb8e45]58 .description = "Print breakpoint table.",
59 .func = cmd_print_breakpoints,
60 .argc = 0,
61};
62
63static cmd_arg_t del_argv = {
64 .type = ARG_TYPE_INT
65};
[da1bafb]66
[5bb8e45]67static cmd_info_t delbkpt_info = {
68 .name = "delbkpt",
[da1bafb]69 .description = "Delete breakpoint.",
[5bb8e45]70 .func = cmd_del_breakpoint,
71 .argc = 1,
72 .argv = &del_argv
73};
74
75static cmd_arg_t add_argv = {
76 .type = ARG_TYPE_INT
77};
[da1bafb]78
[5bb8e45]79static cmd_info_t addbkpt_info = {
80 .name = "addbkpt",
[f10edae]81 .description = "Add bkpoint (break on j/branch insts unsupported).",
[5bb8e45]82 .func = cmd_add_breakpoint,
83 .argc = 1,
84 .argv = &add_argv
85};
86
[07bd114e]87static cmd_arg_t adde_argv[] = {
88 { .type = ARG_TYPE_INT },
89 { .type = ARG_TYPE_INT }
90};
91static cmd_info_t addbkpte_info = {
92 .name = "addbkpte",
[da1bafb]93 .description = "Add bkpoint with a trigger function.",
[07bd114e]94 .func = cmd_add_breakpoint,
95 .argc = 2,
96 .argv = adde_argv
97};
[53c8d12]98#endif
[07bd114e]99
100static struct {
[7f1c620]101 uint32_t andmask;
102 uint32_t value;
[1e1e5e1]103} jmpinstr[] = {
[3bacee1]104 { 0xf3ff0000, 0x41000000 }, /* BCzF */
105 { 0xf3ff0000, 0x41020000 }, /* BCzFL */
106 { 0xf3ff0000, 0x41010000 }, /* BCzT */
107 { 0xf3ff0000, 0x41030000 }, /* BCzTL */
108 { 0xfc000000, 0x10000000 }, /* BEQ */
109 { 0xfc000000, 0x50000000 }, /* BEQL */
110 { 0xfc1f0000, 0x04010000 }, /* BEQL */
111 { 0xfc1f0000, 0x04110000 }, /* BGEZAL */
112 { 0xfc1f0000, 0x04130000 }, /* BGEZALL */
113 { 0xfc1f0000, 0x04030000 }, /* BGEZL */
114 { 0xfc1f0000, 0x1c000000 }, /* BGTZ */
115 { 0xfc1f0000, 0x5c000000 }, /* BGTZL */
116 { 0xfc1f0000, 0x18000000 }, /* BLEZ */
117 { 0xfc1f0000, 0x58000000 }, /* BLEZL */
118 { 0xfc1f0000, 0x04000000 }, /* BLTZ */
119 { 0xfc1f0000, 0x04100000 }, /* BLTZAL */
120 { 0xfc1f0000, 0x04120000 }, /* BLTZALL */
121 { 0xfc1f0000, 0x04020000 }, /* BLTZL */
122 { 0xfc000000, 0x14000000 }, /* BNE */
123 { 0xfc000000, 0x54000000 }, /* BNEL */
124 { 0xfc000000, 0x08000000 }, /* J */
125 { 0xfc000000, 0x0c000000 }, /* JAL */
126 { 0xfc1f07ff, 0x00000009 }, /* JALR */
127 { 0, 0 } /* end of table */
[07bd114e]128};
129
130/** Test, if the given instruction is a jump or branch instruction
131 *
132 * @param instr Instruction code
[da1bafb]133 *
134 * @return true if it is jump instruction, false otherwise
[76fca31]135 *
[07bd114e]136 */
[96b02eb9]137bool is_jump(sysarg_t instr)
[07bd114e]138{
[da1bafb]139 unsigned int i;
[a35b458]140
[1e1e5e1]141 for (i = 0; jmpinstr[i].andmask; i++) {
[07bd114e]142 if ((instr & jmpinstr[i].andmask) == jmpinstr[i].value)
143 return true;
144 }
[a35b458]145
[07bd114e]146 return false;
147}
148
[7328ff4]149static inline void write_inst(uintptr_t addr, uint32_t inst)
150{
151 *((uint32_t *) addr) = inst;
152 smc_coherence((uint32_t *) addr, 4);
153}
154
[53c8d12]155#ifdef CONFIG_KCONSOLE
156
[da1bafb]157/** Add new breakpoint to table
158 *
159 */
[5bb8e45]160int cmd_add_breakpoint(cmd_arg_t *argv)
161{
162 if (argv->intval & 0x3) {
163 printf("Not aligned instruction, forgot to use &symbol?\n");
164 return 1;
165 }
[a35b458]166
[da1bafb]167 irq_spinlock_lock(&bkpoint_lock, true);
[a35b458]168
[5bb8e45]169 /* Check, that the breakpoints do not conflict */
[da1bafb]170 unsigned int i;
[f4c2b6a]171 for (i = 0; i < BKPOINTS_MAX; i++) {
[da1bafb]172 if (breakpoints[i].address == (uintptr_t) argv->intval) {
[5bb8e45]173 printf("Duplicate breakpoint %d.\n", i);
[da1bafb]174 irq_spinlock_unlock(&bkpoint_lock, true);
[5bb8e45]175 return 0;
[da1bafb]176 } else if ((breakpoints[i].address == (uintptr_t) argv->intval +
[96b02eb9]177 sizeof(sysarg_t)) || (breakpoints[i].address ==
178 (uintptr_t) argv->intval - sizeof(sysarg_t))) {
[1e1e5e1]179 printf("Adjacent breakpoints not supported, conflict "
180 "with %d.\n", i);
[da1bafb]181 irq_spinlock_unlock(&bkpoint_lock, true);
[5bb8e45]182 return 0;
183 }
[a35b458]184
[5bb8e45]185 }
[a35b458]186
[da1bafb]187 bpinfo_t *cur = NULL;
[a35b458]188
[da1bafb]189 for (i = 0; i < BKPOINTS_MAX; i++) {
[5bb8e45]190 if (!breakpoints[i].address) {
191 cur = &breakpoints[i];
192 break;
193 }
[da1bafb]194 }
[a35b458]195
[5bb8e45]196 if (!cur) {
197 printf("Too many breakpoints.\n");
[da1bafb]198 irq_spinlock_unlock(&bkpoint_lock, true);
[5bb8e45]199 return 0;
200 }
[a35b458]201
[7e752b2]202 printf("Adding breakpoint on address %p\n", (void *) argv->intval);
[a35b458]203
[7f1c620]204 cur->address = (uintptr_t) argv->intval;
[96b02eb9]205 cur->instruction = ((sysarg_t *) cur->address)[0];
206 cur->nextinstruction = ((sysarg_t *) cur->address)[1];
[07bd114e]207 if (argv == &add_argv) {
208 cur->flags = 0;
[da1bafb]209 } else { /* We are add extended */
[07bd114e]210 cur->flags = BKPOINT_FUNCCALL;
[1e1e5e1]211 cur->bkfunc = (void (*)(void *, istate_t *)) argv[1].intval;
[07bd114e]212 }
[a35b458]213
[07bd114e]214 if (is_jump(cur->instruction))
215 cur->flags |= BKPOINT_ONESHOT;
[a35b458]216
[07bd114e]217 cur->counter = 0;
[a35b458]218
[5bb8e45]219 /* Set breakpoint */
[7328ff4]220 write_inst(cur->address, 0x0d);
[a35b458]221
[da1bafb]222 irq_spinlock_unlock(&bkpoint_lock, true);
[a35b458]223
[5bb8e45]224 return 1;
225}
226
[da1bafb]227/** Remove breakpoint from table
228 *
229 */
[5bb8e45]230int cmd_del_breakpoint(cmd_arg_t *argv)
231{
[6c441cf8]232 if (argv->intval > BKPOINTS_MAX) {
[5bb8e45]233 printf("Invalid breakpoint number.\n");
234 return 0;
235 }
[a35b458]236
[da1bafb]237 irq_spinlock_lock(&bkpoint_lock, true);
[a35b458]238
[da1bafb]239 bpinfo_t *cur = &breakpoints[argv->intval];
[5bb8e45]240 if (!cur->address) {
241 printf("Breakpoint does not exist.\n");
[da1bafb]242 irq_spinlock_unlock(&bkpoint_lock, true);
[5bb8e45]243 return 0;
244 }
[a35b458]245
[07bd114e]246 if ((cur->flags & BKPOINT_INPROG) && (cur->flags & BKPOINT_ONESHOT)) {
247 printf("Cannot remove one-shot breakpoint in-progress\n");
[da1bafb]248 irq_spinlock_unlock(&bkpoint_lock, true);
[07bd114e]249 return 0;
250 }
[a35b458]251
[7328ff4]252 write_inst(cur->address, cur->instruction);
253 write_inst(cur->address + 4, cur->nextinstruction);
[a35b458]254
[0b4a67a]255 cur->address = (uintptr_t) NULL;
[a35b458]256
[da1bafb]257 irq_spinlock_unlock(&bkpoint_lock, true);
[5bb8e45]258 return 1;
259}
260
[da1bafb]261/** Print table of active breakpoints
262 *
263 */
[5bb8e45]264int cmd_print_breakpoints(cmd_arg_t *argv)
265{
[c053f615]266 unsigned int i;
[a35b458]267
[ccb426c]268 printf("[nr] [count] [address ] [inprog] [oneshot] [funccall] [in symbol\n");
[a35b458]269
[a000878c]270 for (i = 0; i < BKPOINTS_MAX; i++) {
[5bb8e45]271 if (breakpoints[i].address) {
[a000878c]272 const char *symbol = symtab_fmt_name_lookup(
[e16e0d59]273 breakpoints[i].address);
[a35b458]274
[7e752b2]275 printf("%-4u %7zu %p %-8s %-9s %-10s %s\n", i,
276 breakpoints[i].counter, (void *) breakpoints[i].address,
[1e1e5e1]277 ((breakpoints[i].flags & BKPOINT_INPROG) ? "true" :
[3bacee1]278 "false"), ((breakpoints[i].flags & BKPOINT_ONESHOT) ?
279 "true" : "false"), ((breakpoints[i].flags &
[1e1e5e1]280 BKPOINT_FUNCCALL) ? "true" : "false"), symbol);
[5bb8e45]281 }
[a000878c]282 }
[a35b458]283
[5bb8e45]284 return 1;
285}
286
[da1bafb]287#endif /* CONFIG_KCONSOLE */
[76fca31]288
[da1bafb]289/** Initialize debugger
290 *
291 */
[193d280c]292void debugger_init(void)
[5bb8e45]293{
[da1bafb]294 unsigned int i;
[a35b458]295
[1e1e5e1]296 for (i = 0; i < BKPOINTS_MAX; i++)
[0b4a67a]297 breakpoints[i].address = (uintptr_t) NULL;
[a35b458]298
[76fca31]299#ifdef CONFIG_KCONSOLE
[0132630]300 cmd_initialize(&bkpts_info);
301 if (!cmd_register(&bkpts_info))
[b2fa1204]302 log(LF_OTHER, LVL_WARN, "Cannot register command %s",
303 bkpts_info.name);
[a35b458]304
[5bb8e45]305 cmd_initialize(&delbkpt_info);
306 if (!cmd_register(&delbkpt_info))
[b2fa1204]307 log(LF_OTHER, LVL_WARN, "Cannot register command %s",
308 delbkpt_info.name);
[a35b458]309
[5bb8e45]310 cmd_initialize(&addbkpt_info);
311 if (!cmd_register(&addbkpt_info))
[b2fa1204]312 log(LF_OTHER, LVL_WARN, "Cannot register command %s",
313 addbkpt_info.name);
[a35b458]314
[07bd114e]315 cmd_initialize(&addbkpte_info);
316 if (!cmd_register(&addbkpte_info))
[b2fa1204]317 log(LF_OTHER, LVL_WARN, "Cannot register command %s",
318 addbkpte_info.name);
[da1bafb]319#endif /* CONFIG_KCONSOLE */
[5bb8e45]320}
321
322/** Handle breakpoint
323 *
[da1bafb]324 * Find breakpoint in breakpoint table.
[5bb8e45]325 * If found, call kconsole, set break on next instruction and reexecute.
326 * If we are on "next instruction", set it back on the first and reexecute.
327 * If breakpoint not found in breakpoint table, call kconsole and start
328 * next instruction.
[da1bafb]329 *
[5bb8e45]330 */
[25d7709]331void debugger_bpoint(istate_t *istate)
[5bb8e45]332{
333 /* test branch delay slot */
334 if (cp0_cause_read() & 0x80000000)
[f651e80]335 panic("Breakpoint in branch delay slot not supported.");
[a35b458]336
[da1bafb]337 irq_spinlock_lock(&bkpoint_lock, false);
[a35b458]338
[da1bafb]339 bpinfo_t *cur = NULL;
340 uintptr_t fireaddr = istate->epc;
341 unsigned int i;
[a35b458]342
[1e1e5e1]343 for (i = 0; i < BKPOINTS_MAX; i++) {
[07bd114e]344 /* Normal breakpoint */
[da1bafb]345 if ((fireaddr == breakpoints[i].address) &&
346 (!(breakpoints[i].flags & BKPOINT_REINST))) {
[5bb8e45]347 cur = &breakpoints[i];
[07bd114e]348 break;
349 }
[a35b458]350
[07bd114e]351 /* Reinst only breakpoint */
[1e1e5e1]352 if ((breakpoints[i].flags & BKPOINT_REINST) &&
[96b02eb9]353 (fireaddr == breakpoints[i].address + sizeof(sysarg_t))) {
[07bd114e]354 cur = &breakpoints[i];
355 break;
356 }
[5bb8e45]357 }
[a35b458]358
[5bb8e45]359 if (cur) {
[07bd114e]360 if (cur->flags & BKPOINT_REINST) {
[5bb8e45]361 /* Set breakpoint on first instruction */
[7328ff4]362 write_inst(cur->address, 0x0d);
[a35b458]363
[5bb8e45]364 /* Return back the second */
[7328ff4]365 write_inst(cur->address + 4, cur->nextinstruction);
[a35b458]366
[07bd114e]367 cur->flags &= ~BKPOINT_REINST;
[da1bafb]368 irq_spinlock_unlock(&bkpoint_lock, false);
[5bb8e45]369 return;
[da1bafb]370 }
[a35b458]371
[07bd114e]372 if (cur->flags & BKPOINT_INPROG)
373 printf("Warning: breakpoint recursion\n");
[a35b458]374
[e2b762ec]375 if (!(cur->flags & BKPOINT_FUNCCALL)) {
[7e752b2]376 printf("***Breakpoint %u: %p in %s.\n", i,
377 (void *) fireaddr,
[da1bafb]378 symtab_fmt_name_lookup(fireaddr));
[e2b762ec]379 }
[a35b458]380
[07bd114e]381 /* Return first instruction back */
[7328ff4]382 write_inst(cur->address, cur->instruction);
[07bd114e]383
[3bacee1]384 if (!(cur->flags & BKPOINT_ONESHOT)) {
[07bd114e]385 /* Set Breakpoint on next instruction */
[7328ff4]386 write_inst(cur->address + 4, 0x0d);
[07bd114e]387 cur->flags |= BKPOINT_REINST;
[1b20da0]388 }
[07bd114e]389 cur->flags |= BKPOINT_INPROG;
[5bb8e45]390 } else {
[7e752b2]391 printf("***Breakpoint %d: %p in %s.\n", i,
392 (void *) fireaddr,
[e16e0d59]393 symtab_fmt_name_lookup(fireaddr));
[a35b458]394
[5bb8e45]395 /* Move on to next instruction */
[25d7709]396 istate->epc += 4;
[5bb8e45]397 }
[a35b458]398
[9f3b880]399 if (cur)
400 cur->counter++;
[a35b458]401
[07bd114e]402 if (cur && (cur->flags & BKPOINT_FUNCCALL)) {
403 /* Allow zero bkfunc, just for counting */
404 if (cur->bkfunc)
[25d7709]405 cur->bkfunc(cur, istate);
[07bd114e]406 } else {
[76fca31]407#ifdef CONFIG_KCONSOLE
[da1bafb]408 /*
409 * This disables all other processors - we are not SMP,
[07bd114e]410 * actually this gets us to cpu_halt, if scheduler() is run
411 * - we generally do not want scheduler to be run from debug,
412 * so this is a good idea
[da1bafb]413 */
[e3306d04]414 atomic_store(&haltstate, 1);
[da1bafb]415 irq_spinlock_unlock(&bkpoint_lock, false);
[a35b458]416
[0b6eba5]417 kconsole("debug", "Debug console ready.\n", false);
[a35b458]418
[da1bafb]419 irq_spinlock_lock(&bkpoint_lock, false);
[e3306d04]420 atomic_store(&haltstate, 0);
[76fca31]421#endif
[07bd114e]422 }
[a35b458]423
[3bacee1]424 if ((cur) && (cur->address == fireaddr) &&
425 ((cur->flags & BKPOINT_INPROG))) {
[07bd114e]426 /* Remove one-shot breakpoint */
427 if ((cur->flags & BKPOINT_ONESHOT))
[0b4a67a]428 cur->address = (uintptr_t) NULL;
[a35b458]429
[07bd114e]430 /* Remove in-progress flag */
431 cur->flags &= ~BKPOINT_INPROG;
[da1bafb]432 }
[a35b458]433
[da1bafb]434 irq_spinlock_unlock(&bkpoint_lock, false);
[5bb8e45]435}
[b45c443]436
[06e1e95]437/** @}
[b45c443]438 */
Note: See TracBrowser for help on using the repository browser.