cregit-Linux how code gets into the kernel

Release 4.15 kernel/debug/kdb/kdb_main.c

Directory: kernel/debug/kdb
/*
 * Kernel Debugger Architecture Independent Main Code
 *
 * 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) 2000 Stephane Eranian <eranian@hpl.hp.com>
 * Xscale (R) modifications copyright (C) 2003 Intel Corporation.
 * Copyright (c) 2009 Wind River Systems, Inc.  All Rights Reserved.
 */

#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/kmsg_dump.h>
#include <linux/reboot.h>
#include <linux/sched.h>
#include <linux/sched/loadavg.h>
#include <linux/sched/stat.h>
#include <linux/sched/debug.h>
#include <linux/sysrq.h>
#include <linux/smp.h>
#include <linux/utsname.h>
#include <linux/vmalloc.h>
#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/kallsyms.h>
#include <linux/kgdb.h>
#include <linux/kdb.h>
#include <linux/notifier.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/nmi.h>
#include <linux/time.h>
#include <linux/ptrace.h>
#include <linux/sysctl.h>
#include <linux/cpu.h>
#include <linux/kdebug.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include "kdb_private.h"


#undef	MODULE_PARAM_PREFIX

#define	MODULE_PARAM_PREFIX "kdb."


static int kdb_cmd_enabled = CONFIG_KDB_DEFAULT_ENABLE;
module_param_named(cmd_enable, kdb_cmd_enabled, int, 0600);


char kdb_grep_string[KDB_GREP_STRLEN];

int kdb_grepping_flag;

EXPORT_SYMBOL(kdb_grepping_flag);

int kdb_grep_leading;

int kdb_grep_trailing;

/*
 * Kernel debugger state flags
 */

int kdb_flags;

/*
 * kdb_lock protects updates to kdb_initial_cpu.  Used to
 * single thread processors through the kernel debugger.
 */

int kdb_initial_cpu = -1;	
/* cpu number that owns kdb */

int kdb_nextline = 1;

int kdb_state;			
/* General KDB state */


struct task_struct *kdb_current_task;

EXPORT_SYMBOL(kdb_current_task);

struct pt_regs *kdb_current_regs;


const char *kdb_diemsg;

static int kdb_go_count;
#ifdef CONFIG_KDB_CONTINUE_CATASTROPHIC

static unsigned int kdb_continue_catastrophic =
	CONFIG_KDB_CONTINUE_CATASTROPHIC;
#else

static unsigned int kdb_continue_catastrophic;
#endif

/* kdb_commands describes the available commands. */

static kdbtab_t *kdb_commands;

#define KDB_BASE_CMD_MAX 50

static int kdb_max_commands = KDB_BASE_CMD_MAX;

static kdbtab_t kdb_base_commands[KDB_BASE_CMD_MAX];

#define for_each_kdbcmd(cmd, num)					\
	for ((cmd) = kdb_base_commands, (num) = 0;                      \
             num < kdb_max_commands;                                    \
             num++, num == KDB_BASE_CMD_MAX ? cmd = kdb_commands : cmd++)


typedef struct _kdbmsg {
	
int	km_diag;	/* kdb diagnostic */
	
char	*km_msg;	/* Corresponding message text */

} kdbmsg_t;


#define KDBMSG(msgnum, text) \
	{ KDB_##msgnum, text }


static kdbmsg_t kdbmsgs[] = {
	KDBMSG(NOTFOUND, "Command Not Found"),
	KDBMSG(ARGCOUNT, "Improper argument count, see usage."),
	KDBMSG(BADWIDTH, "Illegal value for BYTESPERWORD use 1, 2, 4 or 8, "
	       "8 is only allowed on 64 bit systems"),
	KDBMSG(BADRADIX, "Illegal value for RADIX use 8, 10 or 16"),
	KDBMSG(NOTENV, "Cannot find environment variable"),
	KDBMSG(NOENVVALUE, "Environment variable should have value"),
	KDBMSG(NOTIMP, "Command not implemented"),
	KDBMSG(ENVFULL, "Environment full"),
	KDBMSG(ENVBUFFULL, "Environment buffer full"),
	KDBMSG(TOOMANYBPT, "Too many breakpoints defined"),
#ifdef CONFIG_CPU_XSCALE
	KDBMSG(TOOMANYDBREGS, "More breakpoints than ibcr registers defined"),
#else
	KDBMSG(TOOMANYDBREGS, "More breakpoints than db registers defined"),
#endif
	KDBMSG(DUPBPT, "Duplicate breakpoint address"),
	KDBMSG(BPTNOTFOUND, "Breakpoint not found"),
	KDBMSG(BADMODE, "Invalid IDMODE"),
	KDBMSG(BADINT, "Illegal numeric value"),
	KDBMSG(INVADDRFMT, "Invalid symbolic address format"),
	KDBMSG(BADREG, "Invalid register name"),
	KDBMSG(BADCPUNUM, "Invalid cpu number"),
	KDBMSG(BADLENGTH, "Invalid length field"),
	KDBMSG(NOBP, "No Breakpoint exists"),
	KDBMSG(BADADDR, "Invalid address"),
	KDBMSG(NOPERM, "Permission denied"),
};

#undef KDBMSG


static const int __nkdb_err = ARRAY_SIZE(kdbmsgs);


/*
 * Initial environment.   This is all kept static and local to
 * this file.   We don't want to rely on the memory allocation
 * mechanisms in the kernel, so we use a very limited allocate-only
 * heap for new and altered environment variables.  The entire
 * environment is limited to a fixed number of entries (add more
 * to __env[] if required) and a fixed amount of heap (add more to
 * KDB_ENVBUFSIZE if required).
 */


static char *__env[] = {
#if defined(CONFIG_SMP)
 "PROMPT=[%d]kdb> ",
#else
 "PROMPT=kdb> ",
#endif
 "MOREPROMPT=more> ",
 "RADIX=16",
 "MDCOUNT=8",			/* lines of md output */
 KDB_PLATFORM_ENV,
 "DTABCOUNT=30",
 "NOSECT=1",
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
 (char *)0,
};


static const int __nenv = ARRAY_SIZE(__env);


struct task_struct *kdb_curr_task(int cpu) { struct task_struct *p = curr_task(cpu); #ifdef _TIF_MCA_INIT if ((task_thread_info(p)->flags & _TIF_MCA_INIT) && KDB_TSK(cpu)) p = krp->p; #endif return p; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel52100.00%1100.00%
Total52100.00%1100.00%

/* * Check whether the flags of the current command and the permissions * of the kdb console has allow a command to be run. */
static inline bool kdb_check_flags(kdb_cmdflags_t flags, int permissions, bool no_args) { /* permissions comes from userspace so needs massaging slightly */ permissions &= KDB_ENABLE_MASK; permissions |= KDB_ENABLE_ALWAYS_SAFE; /* some commands change group when launched with no arguments */ if (no_args) permissions |= permissions << KDB_ENABLE_NO_ARGS_SHIFT; flags |= KDB_ENABLE_ALL; return permissions & flags; }

Contributors

PersonTokensPropCommitsCommitProp
Daniel R Thompson45100.00%1100.00%
Total45100.00%1100.00%

/* * kdbgetenv - This function will return the character string value of * an environment variable. * Parameters: * match A character string representing an environment variable. * Returns: * NULL No environment variable matches 'match' * char* Pointer to string value of environment variable. */
char *kdbgetenv(const char *match) { char **ep = __env; int matchlen = strlen(match); int i; for (i = 0; i < __nenv; i++) { char *e = *ep++; if (!e) continue; if ((strncmp(match, e, matchlen) == 0) && ((e[matchlen] == '\0') || (e[matchlen] == '='))) { char *cp = strchr(e, '='); return cp ? ++cp : ""; } } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel117100.00%1100.00%
Total117100.00%1100.00%

/* * kdballocenv - This function is used to allocate bytes for * environment entries. * Parameters: * match A character string representing a numeric value * Outputs: * *value the unsigned long representation of the env variable 'match' * Returns: * Zero on success, a kdb diagnostic on failure. * Remarks: * We use a static environment buffer (envbuffer) to hold the values * of dynamically generated environment variables (see kdb_set). Buffer * space once allocated is never free'd, so over time, the amount of space * (currently 512 bytes) will be exhausted if env variables are changed * frequently. */
static char *kdballocenv(size_t bytes) { #define KDB_ENVBUFSIZE 512 static char envbuffer[KDB_ENVBUFSIZE]; static int envbufsize; char *ep = NULL; if ((KDB_ENVBUFSIZE - envbufsize) >= bytes) { ep = &envbuffer[envbufsize]; envbufsize += bytes; } return ep; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel58100.00%1100.00%
Total58100.00%1100.00%

/* * kdbgetulenv - This function will return the value of an unsigned * long-valued environment variable. * Parameters: * match A character string representing a numeric value * Outputs: * *value the unsigned long represntation of the env variable 'match' * Returns: * Zero on success, a kdb diagnostic on failure. */
static int kdbgetulenv(const char *match, unsigned long *value) { char *ep; ep = kdbgetenv(match); if (!ep) return KDB_NOTENV; if (strlen(ep) == 0) return KDB_NOENVVALUE; *value = simple_strtoul(ep, NULL, 0); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel62100.00%1100.00%
Total62100.00%1100.00%

/* * kdbgetintenv - This function will return the value of an * integer-valued environment variable. * Parameters: * match A character string representing an integer-valued env variable * Outputs: * *value the integer representation of the environment variable 'match' * Returns: * Zero on success, a kdb diagnostic on failure. */
int kdbgetintenv(const char *match, int *value) { unsigned long val; int diag; diag = kdbgetulenv(match, &val); if (!diag) *value = (int) val; return diag; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel47100.00%1100.00%
Total47100.00%1100.00%

/* * kdbgetularg - This function will convert a numeric string into an * unsigned long value. * Parameters: * arg A character string representing a numeric value * Outputs: * *value the unsigned long represntation of arg. * Returns: * Zero on success, a kdb diagnostic on failure. */
int kdbgetularg(const char *arg, unsigned long *value) { char *endp; unsigned long val; val = simple_strtoul(arg, &endp, 0); if (endp == arg) { /* * Also try base 16, for us folks too lazy to type the * leading 0x... */ val = simple_strtoul(arg, &endp, 16); if (endp == arg) return KDB_BADINT; } *value = val; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel73100.00%2100.00%
Total73100.00%2100.00%


int kdbgetu64arg(const char *arg, u64 *value) { char *endp; u64 val; val = simple_strtoull(arg, &endp, 0); if (endp == arg) { val = simple_strtoull(arg, &endp, 16); if (endp == arg) return KDB_BADINT; } *value = val; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel70100.00%1100.00%
Total70100.00%1100.00%

/* * kdb_set - This function implements the 'set' command. Alter an * existing environment variable or create a new one. */
int kdb_set(int argc, const char **argv) { int i; char *ep; size_t varlen, vallen; /* * we can be invoked two ways: * set var=value argv[1]="var", argv[2]="value" * set var = value argv[1]="var", argv[2]="=", argv[3]="value" * - if the latter, shift 'em down. */ if (argc == 3) { argv[2] = argv[3]; argc--; } if (argc != 2) return KDB_ARGCOUNT; /* * Check for internal variables */ if (strcmp(argv[1], "KDBDEBUG") == 0) { unsigned int debugflags; char *cp; debugflags = simple_strtoul(argv[2], &cp, 0); if (cp == argv[2] || debugflags & ~KDB_DEBUG_FLAG_MASK) { kdb_printf("kdb: illegal debug flags '%s'\n", argv[2]); return 0; } kdb_flags = (kdb_flags & ~(KDB_DEBUG_FLAG_MASK << KDB_DEBUG_FLAG_SHIFT)) | (debugflags << KDB_DEBUG_FLAG_SHIFT); return 0; } /* * Tokenizer squashed the '=' sign. argv[1] is variable * name, argv[2] = value. */ varlen = strlen(argv[1]); vallen = strlen(argv[2]); ep = kdballocenv(varlen + vallen + 2); if (ep == (char *)0) return KDB_ENVBUFFULL; sprintf(ep, "%s=%s", argv[1], argv[2]); ep[varlen+vallen+1] = '\0'; for (i = 0; i < __nenv; i++) { if (__env[i] && ((strncmp(__env[i], argv[1], varlen) == 0) && ((__env[i][varlen] == '\0') || (__env[i][varlen] == '=')))) { __env[i] = ep; return 0; } } /* * Wasn't existing variable. Fit into slot. */ for (i = 0; i < __nenv-1; i++) { if (__env[i] == (char *)0) { __env[i] = ep; return 0; } } return KDB_ENVFULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel348100.00%1100.00%
Total348100.00%1100.00%


static int kdb_check_regs(void) { if (!kdb_current_regs) { kdb_printf("No current kdb registers." " You may need to select another task\n"); return KDB_BADREG; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel27100.00%1100.00%
Total27100.00%1100.00%

/* * kdbgetaddrarg - This function is responsible for parsing an * address-expression and returning the value of the expression, * symbol name, and offset to the caller. * * The argument may consist of a numeric value (decimal or * hexidecimal), a symbol name, a register name (preceded by the * percent sign), an environment variable with a numeric value * (preceded by a dollar sign) or a simple arithmetic expression * consisting of a symbol name, +/-, and a numeric constant value * (offset). * Parameters: * argc - count of arguments in argv * argv - argument vector * *nextarg - index to next unparsed argument in argv[] * regs - Register state at time of KDB entry * Outputs: * *value - receives the value of the address-expression * *offset - receives the offset specified, if any * *name - receives the symbol name, if any * *nextarg - index to next unparsed argument in argv[] * Returns: * zero is returned on success, a kdb diagnostic code is * returned on error. */
int kdbgetaddrarg(int argc, const char **argv, int *nextarg, unsigned long *value, long *offset, char **name) { unsigned long addr; unsigned long off = 0; int positive; int diag; int found = 0; char *symname; char symbol = '\0'; char *cp; kdb_symtab_t symtab; /* * If the enable flags prohibit both arbitrary memory access * and flow control then there are no reasonable grounds to * provide symbol lookup. */ if (!kdb_check_flags(KDB_ENABLE_MEM_READ | KDB_ENABLE_FLOW_CTRL, kdb_cmd_enabled, false)) return KDB_NOPERM; /* * Process arguments which follow the following syntax: * * symbol | numeric-address [+/- numeric-offset] * %register * $environment-variable */ if (*nextarg > argc) return KDB_ARGCOUNT; symname = (char *)argv[*nextarg]; /* * If there is no whitespace between the symbol * or address and the '+' or '-' symbols, we * remember the character and replace it with a * null so the symbol/value can be properly parsed */ cp = strpbrk(symname, "+-"); if (cp != NULL) { symbol = *cp; *cp++ = '\0'; } if (symname[0] == '$') { diag = kdbgetulenv(&symname[1], &addr); if (diag) return diag; } else if (symname[0] == '%') { diag = kdb_check_regs(); if (diag) return diag; /* Implement register values with % at a later time as it is * arch optional. */ return KDB_NOTIMP; } else { found = kdbgetsymval(symname, &symtab); if (found) { addr = symtab.sym_start; } else { diag = kdbgetularg(argv[*nextarg], &addr); if (diag) return diag; } } if (!found) found = kdbnearsym(addr, &symtab); (*nextarg)++; if (name) *name = symname; if (value) *value = addr; if (offset && name && *name) *offset = addr - symtab.sym_start; if ((*nextarg > argc) && (symbol == '\0')) return 0; /* * check for +/- and offset */ if (symbol == '\0') { if ((argv[*nextarg][0] != '+') && (argv[*nextarg][0] != '-')) { /* * Not our argument. Return. */ return 0; } else { positive = (argv[*nextarg][0] == '+'); (*nextarg)++; } } else positive = (symbol == '+'); /* * Now there must be an offset! */ if ((*nextarg > argc) && (symbol == '\0')) { return KDB_INVADDRFMT; } if (!symbol) { cp = (char *)argv[*nextarg]; (*nextarg)++; } diag = kdbgetularg(cp, &off); if (diag) return diag; if (!positive) off = -off; if (offset) *offset += off; if (value) *value += off; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel47596.35%150.00%
Anton Vorontsov183.65%150.00%
Total493100.00%2100.00%


static void kdb_cmderror(int diag) { int i; if (diag >= 0) { kdb_printf("no error detected (diagnostic is %d)\n", diag); return; } for (i = 0; i < __nkdb_err; i++) { if (kdbmsgs[i].km_diag == diag) { kdb_printf("diag: %d: %s\n", diag, kdbmsgs[i].km_msg); return; } } kdb_printf("Unknown diag %d\n", -diag); }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel79100.00%1100.00%
Total79100.00%1100.00%

/* * kdb_defcmd, kdb_defcmd2 - This function implements the 'defcmd' * command which defines one command as a set of other commands, * terminated by endefcmd. kdb_defcmd processes the initial * 'defcmd' command, kdb_defcmd2 is invoked from kdb_parse for * the following commands until 'endefcmd'. * Inputs: * argc argument count * argv argument vector * Returns: * zero for success, a kdb diagnostic if error */ struct defcmd_set { int count; int usable; char *name; char *usage; char *help; char **command; }; static struct defcmd_set *defcmd_set; static int defcmd_set_count; static int defcmd_in_progress; /* Forward references */ static int kdb_exec_defcmd(int argc, const char **argv);
static int kdb_defcmd2(const char *cmdstr, const char *argv0) { struct defcmd_set *s = defcmd_set + defcmd_set_count - 1; char **save_command = s->command; if (strcmp(argv0, "endefcmd") == 0) { defcmd_in_progress = 0; if (!s->count) s->usable = 0; if (s->usable) /* macros are always safe because when executed each * internal command re-enters kdb_parse() and is * safety checked individually. */ kdb_register_flags(s->name, kdb_exec_defcmd, s->usage, s->help, 0, KDB_ENABLE_ALWAYS_SAFE); return 0; } if (!s->usable) return KDB_NOTIMP; s->command = kzalloc((s->count + 1) * sizeof(*(s->command)), GFP_KDB); if (!s->command) { kdb_printf("Could not allocate new kdb_defcmd table for %s\n", cmdstr); s->usable = 0; return KDB_NOTIMP; } memcpy(s->command, save_command, s->count * sizeof(*(s->command))); s->command[s->count++] = kdb_strdup(cmdstr, GFP_KDB); kfree(save_command); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel20297.58%133.33%
Daniel R Thompson41.93%133.33%
Jovi Zhang10.48%133.33%
Total207100.00%3100.00%


static int kdb_defcmd(int argc, const char **argv) { struct defcmd_set *save_defcmd_set = defcmd_set, *s; if (defcmd_in_progress) { kdb_printf("kdb: nested defcmd detected, assuming missing " "endefcmd\n"); kdb_defcmd2("endefcmd", "endefcmd"); } if (argc == 0) { int i; for (s = defcmd_set; s < defcmd_set + defcmd_set_count; ++s) { kdb_printf("defcmd %s \"%s\" \"%s\"\n", s->name, s->usage, s->help); for (i = 0; i < s->count; ++i) kdb_printf("%s", s->command[i]); kdb_printf("endefcmd\n"); } return 0; } if (argc != 3) return KDB_ARGCOUNT; if (in_dbg_master()) { kdb_printf("Command only available during kdb_init()\n"); return KDB_NOTIMP; } defcmd_set = kmalloc((defcmd_set_count + 1) * sizeof(*defcmd_set), GFP_KDB); if (!defcmd_set) goto fail_defcmd; memcpy(defcmd_set, save_defcmd_set, defcmd_set_count * sizeof(*defcmd_set)); s = defcmd_set + defcmd_set_count; memset(s, 0, sizeof(*s)); s->usable = 1; s->name = kdb_strdup(argv[1], GFP_KDB); if (!s->name) goto fail_name; s->usage = kdb_strdup(argv[2], GFP_KDB); if (!s->usage) goto fail_usage; s->help = kdb_strdup(argv[3], GFP_KDB); if (!s->help) goto fail_help; if (s->usage[0] == '"') { strcpy(s->usage, argv[2]+1); s->usage[strlen(s->usage)-1] = '\0'; } if (s->help[0] == '"') { strcpy(s->help, argv[3]+1); s->help[strlen(s->help)-1] = '\0'; } ++defcmd_set_count; defcmd_in_progress = 1; kfree(save_defcmd_set); return 0; fail_help: kfree(s->usage); fail_usage: kfree(s->name); fail_name: kfree(defcmd_set); fail_defcmd: kdb_printf("Could not allocate new defcmd_set entry for %s\n", argv[1]); defcmd_set = save_defcmd_set; return KDB_NOTIMP; }

Contributors

PersonTokensPropCommitsCommitProp
Jason Wessel432100.00%3100.00%
Total432100.00%3100.00%

/* * kdb_exec_defcmd - Execute the set of commands associated with this * defcmd name. * Inputs: * argc argument count * argv argument vector * Returns: * zero for success, a kdb diagnostic if error */
static int kdb_exec_defcmd(int argc, const char **argv) { int i, ret; struct defcmd_set *s; if (argc != 0) return KDB_ARGCOUNT; for (s = defcmd_set, i = 0; i < defcmd_set_count; ++i