/* * Kernel Debugger Architecture Independent Breakpoint Handler * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved. */ #include #include #include #include #include #include #include #include #include "kdb_private.h" /* * Table of kdb_breakpoints */ kdb_bp_t kdb_breakpoints[KDB_MAXBPT]; static void kdb_setsinglestep(struct pt_regs *regs) { KDB_STATE_SET(DOING_SS); } static char *kdb_rwtypes[] = { "Instruction(i)", "Instruction(Register)", "Data Write", "I/O", "Data Access" }; static char *kdb_bptype(kdb_bp_t *bp) { if (bp->bp_type < 0 || bp->bp_type > 4) return ""; return kdb_rwtypes[bp->bp_type]; } static int kdb_parsebp(int argc, const char **argv, int *nextargp, kdb_bp_t *bp) { int nextarg = *nextargp; int diag; bp->bph_length = 1; if ((argc + 1) != nextarg) { if (strncasecmp(argv[nextarg], "datar", sizeof("datar")) == 0) bp->bp_type = BP_ACCESS_WATCHPOINT; else if (strncasecmp(argv[nextarg], "dataw", sizeof("dataw")) == 0) bp->bp_type = BP_WRITE_WATCHPOINT; else if (strncasecmp(argv[nextarg], "inst", sizeof("inst")) == 0) bp->bp_type = BP_HARDWARE_BREAKPOINT; else return KDB_ARGCOUNT; bp->bph_length = 1; nextarg++; if ((argc + 1) != nextarg) { unsigned long len; diag = kdbgetularg((char *)argv[nextarg], &len); if (diag) return diag; if (len > 8) return KDB_BADLENGTH; bp->bph_length = len; nextarg++; } if ((argc + 1) != nextarg) return KDB_ARGCOUNT; } *nextargp = nextarg; return 0; } static int _kdb_bp_remove(kdb_bp_t *bp) { int ret = 1; if (!bp->bp_installed) return ret; if (!bp->bp_type) ret = dbg_remove_sw_break(bp->bp_addr); else ret = arch_kgdb_ops.remove_hw_breakpoint(bp->bp_addr, bp->bph_length, bp->bp_type); if (ret == 0) bp->bp_installed = 0; return ret; } static void kdb_handle_bp(struct pt_regs *regs, kdb_bp_t *bp) { if (KDB_DEBUG(BP)) kdb_printf("regs->ip = 0x%lx\n", instruction_pointer(regs)); /* * Setup single step */ kdb_setsinglestep(regs); /* * Reset delay attribute */ bp->bp_delay = 0; bp->bp_delayed = 1; } static int _kdb_bp_install(struct pt_regs *regs, kdb_bp_t *bp) { int ret; /* * Install the breakpoint, if it is not already installed. */ if (KDB_DEBUG(BP)) kdb_printf("%s: bp_installed %d\n", __func__, bp->bp_installed); if (!KDB_STATE(SSBPT)) bp->bp_delay = 0; if (bp->bp_installed) return 1; if (bp->bp_delay || (bp->bp_delayed && KDB_STATE(DOING_SS))) { if (KDB_DEBUG(BP)) kdb_printf("%s: delayed bp\n", __func__); kdb_handle_bp(regs, bp); return 0; } if (!bp->bp_type) ret = dbg_set_sw_break(bp->bp_addr); else ret = arch_kgdb_ops.set_hw_breakpoint(bp->bp_addr, bp->bph_length, bp->bp_type); if (ret == 0) { bp->bp_installed = 1; } else { kdb_printf("%s: failed to set breakpoint at 0x%lx\n", __func__, bp->bp_addr); if (!bp->bp_type) { kdb_printf("Software breakpoints are unavailable.\n" " Boot the kernel with rodata=off\n" " OR use hw breaks: help bph\n"); } return 1; } return 0; } /* * kdb_bp_install * * Install kdb_breakpoints prior to returning from the * kernel debugger. This allows the kdb_breakpoints to be set * upon functions that are used internally by kdb, such as * printk(). This function is only called once per kdb session. */ void kdb_bp_install(struct pt_regs *regs) { int i; for (i = 0; i < KDB_MAXBPT; i++) { kdb_bp_t *bp = &kdb_breakpoints[i]; if (KDB_DEBUG(BP)) { kdb_printf("%s: bp %d bp_enabled %d\n", __func__, i, bp->bp_enabled); } if (bp->bp_enabled) _kdb_bp_install(regs, bp); } } /* * kdb_bp_remove * * Remove kdb_breakpoints upon entry to the kernel debugger. * * Parameters: * None. * Outputs: * None. * Returns: * None. * Locking: * None. * Remarks: */ void kdb_bp_remove(void) { int i; for (i = KDB_MAXBPT - 1; i >= 0; i--) { kdb_bp_t *bp = &kdb_breakpoints[i]; if (KDB_DEBUG(BP)) { kdb_printf("%s: bp %d bp_enabled %d\n", __func__, i, bp->bp_enabled); } if (bp->bp_enabled) _kdb_bp_remove(bp); } } /* * kdb_printbp * * Internal function to format and print a breakpoint entry. * * Parameters: * None. * Outputs: * None. * Returns: * None. * Locking: * None. * Remarks: */ static void kdb_printbp(kdb_bp_t *bp, int i) { kdb_printf("%s ", kdb_bptype(bp)); kdb_printf("BP #%d at ", i); kdb_symbol_print(bp->bp_addr, NULL, KDB_SP_DEFAULT); if (bp->bp_enabled) kdb_printf("\n is enabled "); else kdb_printf("\n is disabled"); kdb_printf(" addr at %016lx, hardtype=%d installed=%d\n", bp->bp_addr, bp->bp_type, bp->bp_installed); kdb_printf("\n"); } /* * kdb_bp * * Handle the bp commands. * * [bp|bph] [DATAR|DATAW] * * Parameters: * argc Count of arguments in argv * argv Space delimited command line arguments * Outputs: * None. * Returns: * Zero for success, a kdb diagnostic if failure. * Locking: * None. * Remarks: * * bp Set breakpoint on all cpus. Only use hardware assist if need. * bph Set breakpoint on all cpus. Force hardware register */ static int kdb_bp(int argc, const char **argv) { int i, bpno; kdb_bp_t *bp, *bp_check; int diag; char *symname = NULL; long offset = 0ul; int nextarg; kdb_bp_t template = {0}; if (argc == 0) { /* * Display breakpoint table */ for (bpno = 0, bp = kdb_breakpoints; bpno < KDB_MAXBPT; bpno++, bp++) { if (bp->bp_free) continue; kdb_printbp(bp, bpno); } return 0; } nextarg = 1; diag = kdbgetaddrarg(argc, argv, &nextarg, &template.bp_addr, &offset, &symname); if (diag) return diag; if (!template.bp_addr) return KDB_BADINT; /* * This check is redundant (since the breakpoint machinery should * be doing the same check during kdb_bp_install) but gives the * user immediate feedback. */ diag = kgdb_validate_break_address(template.bp_addr); if (diag) return diag; /* * Find an empty bp structure to allocate */ for (bpno = 0, bp = kdb_breakpoints; bpno < KDB_MAXBPT; bpno++, bp++) { if (bp->bp_free) break; } if (bpno == KDB_MAXBPT) return KDB_TOOMANYBPT; if (strcmp(argv[0], "bph") == 0) { template.bp_type = BP_HARDWARE_BREAKPOINT; diag = kdb_parsebp(argc, argv, &nextarg, &template); if (diag) return diag; } else { template.bp_type = BP_BREAKPOINT; } /* * Check for clashing breakpoints. * * Note, in this design we can't have hardware breakpoints * enabled for both read and write on the same address. */ for (i = 0, bp_check = kdb_breakpoints; i < KDB_MAXBPT; i++, bp_check++) { if (!bp_check->bp_free && bp_check->bp_addr == template.bp_addr) { kdb_printf("You already have a breakpoint at " kdb_bfd_vma_fmt0 "\n", template.bp_addr); return KDB_DUPBPT; } } template.bp_enabled = 1; /* * Actually allocate the breakpoint found earlier */ *bp = template; bp->bp_free = 0; kdb_printbp(bp, bpno); return 0; } /* * kdb_bc * * Handles the 'bc', 'be', and 'bd' commands * * [bd|bc|be] * [bd|bc|be] * * * Parameters: * argc Count of arguments in argv * argv Space delimited command line arguments * Outputs: * None. * Returns: * Zero for success, a kdb diagnostic for failure * Locking: * None. * Remarks: */ static int kdb_bc(int argc, const char **argv) { unsigned long addr; kdb_bp_t *bp = NULL; int lowbp = KDB_MAXBPT; int highbp = 0; int done = 0; int i; int diag = 0; int cmd; /* KDBCMD_B? */ #define KDBCMD_BC 0 #define KDBCMD_BE 1 #define KDBCMD_BD 2 if (strcmp(argv[0], "be") == 0) cmd = KDBCMD_BE; else if (strcmp(argv[0], "bd") == 0) cmd = KDBCMD_BD; else cmd = KDBCMD_BC; if (argc != 1) return KDB_ARGCOUNT; if (strcmp(argv[1], "*") == 0) { lowbp = 0; highbp = KDB_MAXBPT; } else { diag = kdbgetularg(argv[1], &addr); if (diag) return diag; /* * For addresses less than the maximum breakpoint number, * assume that the breakpoint number is desired. */ if (addr < KDB_MAXBPT) { lowbp = highbp = addr; highbp++; } else { for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++) { if (bp->bp_addr == addr) { lowbp = highbp = i; highbp++; break; } } } } /* * Now operate on the set of breakpoints matching the input * criteria (either '*' for all, or an individual breakpoint). */ for (bp = &kdb_breakpoints[lowbp], i = lowbp; i < highbp; i++, bp++) { if (bp->bp_free) continue; done++; switch (cmd) { case KDBCMD_BC: bp->bp_enabled = 0; kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " cleared\n", i, bp->bp_addr); bp->bp_addr = 0; bp->bp_free = 1; break; case KDBCMD_BE: if (bp->bp_enabled) break; bp->bp_enabled = 1; kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " enabled\n", i, bp->bp_addr); break; case KDBCMD_BD: if (!bp->bp_enabled) break; bp->bp_enabled = 0; kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " disabled\n", i, bp->bp_addr); break; } if (bp->bp_delay && (cmd == KDBCMD_BC || cmd == KDBCMD_BD)) { bp->bp_delay = 0; KDB_STATE_CLEAR(SSBPT); } } return (!done) ? KDB_BPTNOTFOUND : 0; } /* * kdb_ss * * Process the 'ss' (Single Step) command. * * ss * * Parameters: * argc Argument count * argv Argument vector * Outputs: * None. * Returns: * KDB_CMD_SS for success, a kdb error if failure. * Locking: * None. * Remarks: * * Set the arch specific option to trigger a debug trap after the next * instruction. */ static int kdb_ss(int argc, const char **argv) { if (argc != 0) return KDB_ARGCOUNT; /* * Set trace flag and go. */ KDB_STATE_SET(DOING_SS); return KDB_CMD_SS; } static kdbtab_t bptab[] = { { .name = "bp", .func = kdb_bp, .usage = "[]", .help = "Set/Display breakpoints", .flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS, }, { .name = "bl", .func = kdb_bp, .usage = "[]", .help = "Display breakpoints", .flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS, }, { .name = "bc", .func = kdb_bc, .usage = "", .help = "Clear Breakpoint", .flags = KDB_ENABLE_FLOW_CTRL, }, { .name = "be", .func = kdb_bc, .usage = "", .help = "Enable Breakpoint", .flags = KDB_ENABLE_FLOW_CTRL, }, { .name = "bd", .func = kdb_bc, .usage = "", .help = "Disable Breakpoint", .flags = KDB_ENABLE_FLOW_CTRL, }, { .name = "ss", .func = kdb_ss, .usage = "", .help = "Single Step", .minlen = 1, .flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS, }, }; static kdbtab_t bphcmd = { .name = "bph", .func = kdb_bp, .usage = "[]", .help = "[datar [length]|dataw [length]] Set hw brk", .flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS, }; /* Initialize the breakpoint table and register breakpoint commands. */ void __init kdb_initbptab(void) { int i; kdb_bp_t *bp; /* * First time initialization. */ memset(&kdb_breakpoints, '\0', sizeof(kdb_breakpoints)); for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++) bp->bp_free = 1; kdb_register_table(bptab, ARRAY_SIZE(bptab)); if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) kdb_register_table(&bphcmd, 1); }