⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 kgdb.c

📁 Kernel code of linux kernel
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * KGDB stub. * * Maintainer: Jason Wessel <jason.wessel@windriver.com> * * Copyright (C) 2000-2001 VERITAS Software Corporation. * Copyright (C) 2002-2004 Timesys Corporation * Copyright (C) 2003-2004 Amit S. Kale <amitkale@linsyssoft.com> * Copyright (C) 2004 Pavel Machek <pavel@suse.cz> * Copyright (C) 2004-2006 Tom Rini <trini@kernel.crashing.org> * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd. * Copyright (C) 2005-2008 Wind River Systems, Inc. * Copyright (C) 2007 MontaVista Software, Inc. * Copyright (C) 2008 Red Hat, Inc., Ingo Molnar <mingo@redhat.com> * * Contributors at various stages not listed above: *  Jason Wessel ( jason.wessel@windriver.com ) *  George Anzinger <george@mvista.com> *  Anurekh Saxena (anurekh.saxena@timesys.com) *  Lake Stevens Instrument Division (Glenn Engel) *  Jim Kingdon, Cygnus Support. * * Original KGDB stub: David Grothe <dave@gcom.com>, * Tigran Aivazian <tigran@sco.com> * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */#include <linux/pid_namespace.h>#include <linux/clocksource.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/console.h>#include <linux/threads.h>#include <linux/uaccess.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/ptrace.h>#include <linux/reboot.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/sysrq.h>#include <linux/init.h>#include <linux/kgdb.h>#include <linux/pid.h>#include <linux/smp.h>#include <linux/mm.h>#include <asm/cacheflush.h>#include <asm/byteorder.h>#include <asm/atomic.h>#include <asm/system.h>#include <asm/unaligned.h>static int kgdb_break_asap;#define KGDB_MAX_THREAD_QUERY 17struct kgdb_state {	int			ex_vector;	int			signo;	int			err_code;	int			cpu;	int			pass_exception;	unsigned long		thr_query;	unsigned long		threadid;	long			kgdb_usethreadid;	struct pt_regs		*linux_regs;};static struct debuggerinfo_struct {	void			*debuggerinfo;	struct task_struct	*task;} kgdb_info[NR_CPUS];/** * kgdb_connected - Is a host GDB connected to us? */int				kgdb_connected;EXPORT_SYMBOL_GPL(kgdb_connected);/* All the KGDB handlers are installed */static int			kgdb_io_module_registered;/* Guard for recursive entry */static int			exception_level;static struct kgdb_io		*kgdb_io_ops;static DEFINE_SPINLOCK(kgdb_registration_lock);/* kgdb console driver is loaded */static int kgdb_con_registered;/* determine if kgdb console output should be used */static int kgdb_use_con;static int __init opt_kgdb_con(char *str){	kgdb_use_con = 1;	return 0;}early_param("kgdbcon", opt_kgdb_con);module_param(kgdb_use_con, int, 0644);/* * Holds information about breakpoints in a kernel. These breakpoints are * added and removed by gdb. */static struct kgdb_bkpt		kgdb_break[KGDB_MAX_BREAKPOINTS] = {	[0 ... KGDB_MAX_BREAKPOINTS-1] = { .state = BP_UNDEFINED }};/* * The CPU# of the active CPU, or -1 if none: */atomic_t			kgdb_active = ATOMIC_INIT(-1);/* * We use NR_CPUs not PERCPU, in case kgdb is used to debug early * bootup code (which might not have percpu set up yet): */static atomic_t			passive_cpu_wait[NR_CPUS];static atomic_t			cpu_in_kgdb[NR_CPUS];atomic_t			kgdb_setting_breakpoint;struct task_struct		*kgdb_usethread;struct task_struct		*kgdb_contthread;int				kgdb_single_step;/* Our I/O buffers. */static char			remcom_in_buffer[BUFMAX];static char			remcom_out_buffer[BUFMAX];/* Storage for the registers, in GDB format. */static unsigned long		gdb_regs[(NUMREGBYTES +					sizeof(unsigned long) - 1) /					sizeof(unsigned long)];/* to keep track of the CPU which is doing the single stepping*/atomic_t			kgdb_cpu_doing_single_step = ATOMIC_INIT(-1);/* * If you are debugging a problem where roundup (the collection of * all other CPUs) is a problem [this should be extremely rare], * then use the nokgdbroundup option to avoid roundup. In that case * the other CPUs might interfere with your debugging context, so * use this with care: */static int kgdb_do_roundup = 1;static int __init opt_nokgdbroundup(char *str){	kgdb_do_roundup = 0;	return 0;}early_param("nokgdbroundup", opt_nokgdbroundup);/* * Finally, some KGDB code :-) *//* * Weak aliases for breakpoint management, * can be overriden by architectures when needed: */int __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr){	int err;	err = probe_kernel_read(saved_instr, (char *)addr, BREAK_INSTR_SIZE);	if (err)		return err;	return probe_kernel_write((char *)addr, arch_kgdb_ops.gdb_bpt_instr,				  BREAK_INSTR_SIZE);}int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle){	return probe_kernel_write((char *)addr,				  (char *)bundle, BREAK_INSTR_SIZE);}int __weak kgdb_validate_break_address(unsigned long addr){	char tmp_variable[BREAK_INSTR_SIZE];	int err;	/* Validate setting the breakpoint and then removing it.  In the	 * remove fails, the kernel needs to emit a bad message because we	 * are deep trouble not being able to put things back the way we	 * found them.	 */	err = kgdb_arch_set_breakpoint(addr, tmp_variable);	if (err)		return err;	err = kgdb_arch_remove_breakpoint(addr, tmp_variable);	if (err)		printk(KERN_ERR "KGDB: Critical breakpoint error, kernel "		   "memory destroyed at: %lx", addr);	return err;}unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs){	return instruction_pointer(regs);}int __weak kgdb_arch_init(void){	return 0;}int __weak kgdb_skipexception(int exception, struct pt_regs *regs){	return 0;}void __weakkgdb_post_primary_code(struct pt_regs *regs, int e_vector, int err_code){	return;}/** *	kgdb_disable_hw_debug - Disable hardware debugging while we in kgdb. *	@regs: Current &struct pt_regs. * *	This function will be called if the particular architecture must *	disable hardware debugging while it is processing gdb packets or *	handling exception. */void __weak kgdb_disable_hw_debug(struct pt_regs *regs){}/* * GDB remote protocol parser: */static int hex(char ch){	if ((ch >= 'a') && (ch <= 'f'))		return ch - 'a' + 10;	if ((ch >= '0') && (ch <= '9'))		return ch - '0';	if ((ch >= 'A') && (ch <= 'F'))		return ch - 'A' + 10;	return -1;}/* scan for the sequence $<data>#<checksum> */static void get_packet(char *buffer){	unsigned char checksum;	unsigned char xmitcsum;	int count;	char ch;	do {		/*		 * Spin and wait around for the start character, ignore all		 * other characters:		 */		while ((ch = (kgdb_io_ops->read_char())) != '$')			/* nothing */;		kgdb_connected = 1;		checksum = 0;		xmitcsum = -1;		count = 0;		/*		 * now, read until a # or end of buffer is found:		 */		while (count < (BUFMAX - 1)) {			ch = kgdb_io_ops->read_char();			if (ch == '#')				break;			checksum = checksum + ch;			buffer[count] = ch;			count = count + 1;		}		buffer[count] = 0;		if (ch == '#') {			xmitcsum = hex(kgdb_io_ops->read_char()) << 4;			xmitcsum += hex(kgdb_io_ops->read_char());			if (checksum != xmitcsum)				/* failed checksum */				kgdb_io_ops->write_char('-');			else				/* successful transfer */				kgdb_io_ops->write_char('+');			if (kgdb_io_ops->flush)				kgdb_io_ops->flush();		}	} while (checksum != xmitcsum);}/* * Send the packet in buffer. * Check for gdb connection if asked for. */static void put_packet(char *buffer){	unsigned char checksum;	int count;	char ch;	/*	 * $<packet info>#<checksum>.	 */	while (1) {		kgdb_io_ops->write_char('$');		checksum = 0;		count = 0;		while ((ch = buffer[count])) {			kgdb_io_ops->write_char(ch);			checksum += ch;			count++;		}		kgdb_io_ops->write_char('#');		kgdb_io_ops->write_char(hex_asc_hi(checksum));		kgdb_io_ops->write_char(hex_asc_lo(checksum));		if (kgdb_io_ops->flush)			kgdb_io_ops->flush();		/* Now see what we get in reply. */		ch = kgdb_io_ops->read_char();		if (ch == 3)			ch = kgdb_io_ops->read_char();		/* If we get an ACK, we are done. */		if (ch == '+')			return;		/*		 * If we get the start of another packet, this means		 * that GDB is attempting to reconnect.  We will NAK		 * the packet being sent, and stop trying to send this		 * packet.		 */		if (ch == '$') {			kgdb_io_ops->write_char('-');			if (kgdb_io_ops->flush)				kgdb_io_ops->flush();			return;		}	}}/* * Convert the memory pointed to by mem into hex, placing result in buf. * Return a pointer to the last char put in buf (null). May return an error. */int kgdb_mem2hex(char *mem, char *buf, int count){	char *tmp;	int err;	/*	 * We use the upper half of buf as an intermediate buffer for the	 * raw memory copy.  Hex conversion will work against this one.	 */	tmp = buf + count;	err = probe_kernel_read(tmp, mem, count);	if (!err) {		while (count > 0) {			buf = pack_hex_byte(buf, *tmp);			tmp++;			count--;		}		*buf = 0;	}	return err;}/* * Copy the binary array pointed to by buf into mem.  Fix $, #, and * 0x7d escaped with 0x7d.  Return a pointer to the character after * the last byte written. */static int kgdb_ebin2mem(char *buf, char *mem, int count){	int err = 0;	char c;	while (count-- > 0) {		c = *buf++;		if (c == 0x7d)			c = *buf++ ^ 0x20;		err = probe_kernel_write(mem, &c, 1);		if (err)			break;		mem++;	}	return err;}/* * Convert the hex array pointed to by buf into binary to be placed in mem. * Return a pointer to the character AFTER the last byte written. * May return an error. */int kgdb_hex2mem(char *buf, char *mem, int count){	char *tmp_raw;	char *tmp_hex;	/*	 * We use the upper half of buf as an intermediate buffer for the	 * raw memory that is converted from hex.	 */	tmp_raw = buf + count * 2;	tmp_hex = tmp_raw - 1;	while (tmp_hex >= buf) {		tmp_raw--;		*tmp_raw = hex(*tmp_hex--);		*tmp_raw |= hex(*tmp_hex--) << 4;	}	return probe_kernel_write(mem, tmp_raw, count);}/* * While we find nice hex chars, build a long_val. * Return number of chars processed. */int kgdb_hex2long(char **ptr, unsigned long *long_val){	int hex_val;	int num = 0;	int negate = 0;	*long_val = 0;	if (**ptr == '-') {		negate = 1;		(*ptr)++;	}	while (**ptr) {		hex_val = hex(**ptr);		if (hex_val < 0)			break;		*long_val = (*long_val << 4) | hex_val;		num++;		(*ptr)++;	}	if (negate)		*long_val = -*long_val;	return num;}/* Write memory due to an 'M' or 'X' packet. */static int write_mem_msg(int binary){	char *ptr = &remcom_in_buffer[1];	unsigned long addr;	unsigned long length;	int err;	if (kgdb_hex2long(&ptr, &addr) > 0 && *(ptr++) == ',' &&	    kgdb_hex2long(&ptr, &length) > 0 && *(ptr++) == ':') {		if (binary)			err = kgdb_ebin2mem(ptr, (char *)addr, length);		else			err = kgdb_hex2mem(ptr, (char *)addr, length);		if (err)			return err;		if (CACHE_FLUSH_IS_SAFE)			flush_icache_range(addr, addr + length);		return 0;	}	return -EINVAL;}static void error_packet(char *pkt, int error){	error = -error;	pkt[0] = 'E';	pkt[1] = hex_asc[(error / 10)];	pkt[2] = hex_asc[(error % 10)];	pkt[3] = '\0';}/* * Thread ID accessors. We represent a flat TID space to GDB, where * the per CPU idle threads (which under Linux all have PID 0) are * remapped to negative TIDs. */#define BUF_THREAD_ID_SIZE	16static char *pack_threadid(char *pkt, unsigned char *id){	char *limit;	limit = pkt + BUF_THREAD_ID_SIZE;	while (pkt < limit)		pkt = pack_hex_byte(pkt, *id++);	return pkt;}static void int_to_threadref(unsigned char *id, int value){	unsigned char *scan;	int i = 4;	scan = (unsigned char *)id;	while (i--)		*scan++ = 0;	put_unaligned_be32(value, scan);}static struct task_struct *getthread(struct pt_regs *regs, int tid){	/*	 * Non-positive TIDs are remapped to the cpu shadow information	 */	if (tid == 0 || tid == -1)		tid = -atomic_read(&kgdb_active) - 2;	if (tid < 0) {		if (kgdb_info[-tid - 2].task)			return kgdb_info[-tid - 2].task;		else			return idle_task(-tid - 2);	}	/*	 * find_task_by_pid_ns() does not take the tasklist lock anymore	 * but is nicely RCU locked - hence is a pretty resilient	 * thing to use:	 */	return find_task_by_pid_ns(tid, &init_pid_ns);}/* * CPU debug state control: */#ifdef CONFIG_SMPstatic void kgdb_wait(struct pt_regs *regs){	unsigned long flags;	int cpu;	local_irq_save(flags);	cpu = raw_smp_processor_id();	kgdb_info[cpu].debuggerinfo = regs;	kgdb_info[cpu].task = current;	/*	 * Make sure the above info reaches the primary CPU before	 * our cpu_in_kgdb[] flag setting does:	 */	smp_wmb();	atomic_set(&cpu_in_kgdb[cpu], 1);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -