linux-next/kernel/debug/kdb/kdb_bp.c
Nir Lichtman 272fad470b kdb: Fix breakpoint enable to be silent if already enabled
Fix the breakpoint enable command (be) to a logic that is inline with the
breakpoint disable command (bd) in which if the breakpoint is already in
an enabled state, do not print the message of enabled again to the user.

Also a small nit fix of the new line in a separate print.

Signed-off-by: Nir Lichtman <nir@lichtman.org>
Link: https://lore.kernel.org/r/20241027204729.GA907155@lichtman.org
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
2024-11-02 08:41:39 +00:00

594 lines
11 KiB
C

/*
* 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 <linux/string.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kdb.h>
#include <linux/kgdb.h>
#include <linux/smp.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#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] <addr-expression> [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] <breakpoint-number>
* [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 = "[<vaddr>]",
.help = "Set/Display breakpoints",
.flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS,
},
{ .name = "bl",
.func = kdb_bp,
.usage = "[<vaddr>]",
.help = "Display breakpoints",
.flags = KDB_ENABLE_FLOW_CTRL | KDB_REPEAT_NO_ARGS,
},
{ .name = "bc",
.func = kdb_bc,
.usage = "<bpnum>",
.help = "Clear Breakpoint",
.flags = KDB_ENABLE_FLOW_CTRL,
},
{ .name = "be",
.func = kdb_bc,
.usage = "<bpnum>",
.help = "Enable Breakpoint",
.flags = KDB_ENABLE_FLOW_CTRL,
},
{ .name = "bd",
.func = kdb_bc,
.usage = "<bpnum>",
.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 = "[<vaddr>]",
.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);
}