diff options
Diffstat (limited to 'kernel/debug/debug_core.c')
| -rw-r--r-- | kernel/debug/debug_core.c | 179 | 
1 files changed, 135 insertions, 44 deletions
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index cefd4a11f6d..1adf62b39b9 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -29,6 +29,7 @@   */  #include <linux/pid_namespace.h>  #include <linux/clocksource.h> +#include <linux/serial_core.h>  #include <linux/interrupt.h>  #include <linux/spinlock.h>  #include <linux/console.h> @@ -41,18 +42,19 @@  #include <linux/delay.h>  #include <linux/sched.h>  #include <linux/sysrq.h> +#include <linux/reboot.h>  #include <linux/init.h>  #include <linux/kgdb.h>  #include <linux/kdb.h>  #include <linux/pid.h>  #include <linux/smp.h>  #include <linux/mm.h> +#include <linux/vmacache.h>  #include <linux/rcupdate.h>  #include <asm/cacheflush.h>  #include <asm/byteorder.h> -#include <asm/atomic.h> -#include <asm/system.h> +#include <linux/atomic.h>  #include "debug_core.h" @@ -75,6 +77,8 @@ static int			exception_level;  struct kgdb_io		*dbg_io_ops;  static DEFINE_SPINLOCK(kgdb_registration_lock); +/* Action for the reboot notifiter, a global allow kdb to change it */ +static int kgdbreboot;  /* kgdb console driver is loaded */  static int kgdb_con_registered;  /* determine if kgdb console output should be used */ @@ -96,6 +100,7 @@ static int __init opt_kgdb_con(char *str)  early_param("kgdbcon", opt_kgdb_con);  module_param(kgdb_use_con, int, 0644); +module_param(kgdbreboot, int, 0644);  /*   * Holds information about breakpoints in a kernel. These breakpoints are @@ -157,37 +162,39 @@ early_param("nokgdbroundup", opt_nokgdbroundup);   * 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 __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)  {  	int err; -	err = probe_kernel_read(saved_instr, (char *)addr, BREAK_INSTR_SIZE); +	err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr, +				BREAK_INSTR_SIZE);  	if (err)  		return err; - -	return probe_kernel_write((char *)addr, arch_kgdb_ops.gdb_bpt_instr, -				  BREAK_INSTR_SIZE); +	err = probe_kernel_write((char *)bpt->bpt_addr, +				 arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE); +	return err;  } -int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) +int __weak kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)  { -	return probe_kernel_write((char *)addr, -				  (char *)bundle, BREAK_INSTR_SIZE); +	return probe_kernel_write((char *)bpt->bpt_addr, +				  (char *)bpt->saved_instr, BREAK_INSTR_SIZE);  }  int __weak kgdb_validate_break_address(unsigned long addr)  { -	char tmp_variable[BREAK_INSTR_SIZE]; +	struct kgdb_bkpt tmp;  	int err; -	/* Validate setting the breakpoint and then removing it.  In the +	/* Validate setting the breakpoint and then removing it.  If 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); +	tmp.bpt_addr = addr; +	err = kgdb_arch_set_breakpoint(&tmp);  	if (err)  		return err; -	err = kgdb_arch_remove_breakpoint(addr, tmp_variable); +	err = kgdb_arch_remove_breakpoint(&tmp);  	if (err)  		printk(KERN_ERR "KGDB: Critical breakpoint error, kernel "  		   "memory destroyed at: %lx", addr); @@ -218,10 +225,17 @@ static void kgdb_flush_swbreak_addr(unsigned long addr)  	if (!CACHE_FLUSH_IS_SAFE)  		return; -	if (current->mm && current->mm->mmap_cache) { -		flush_cache_range(current->mm->mmap_cache, -				  addr, addr + BREAK_INSTR_SIZE); +	if (current->mm) { +		int i; + +		for (i = 0; i < VMACACHE_SIZE; i++) { +			if (!current->vmacache[i]) +				continue; +			flush_cache_range(current->vmacache[i], +					  addr, addr + BREAK_INSTR_SIZE); +		}  	} +  	/* Force flush instruction cache if it was outside the mm */  	flush_icache_range(addr, addr + BREAK_INSTR_SIZE);  } @@ -231,7 +245,6 @@ static void kgdb_flush_swbreak_addr(unsigned long addr)   */  int dbg_activate_sw_breakpoints(void)  { -	unsigned long addr;  	int error;  	int ret = 0;  	int i; @@ -240,16 +253,15 @@ int dbg_activate_sw_breakpoints(void)  		if (kgdb_break[i].state != BP_SET)  			continue; -		addr = kgdb_break[i].bpt_addr; -		error = kgdb_arch_set_breakpoint(addr, -				kgdb_break[i].saved_instr); +		error = kgdb_arch_set_breakpoint(&kgdb_break[i]);  		if (error) {  			ret = error; -			printk(KERN_INFO "KGDB: BP install failed: %lx", addr); +			printk(KERN_INFO "KGDB: BP install failed: %lx", +			       kgdb_break[i].bpt_addr);  			continue;  		} -		kgdb_flush_swbreak_addr(addr); +		kgdb_flush_swbreak_addr(kgdb_break[i].bpt_addr);  		kgdb_break[i].state = BP_ACTIVE;  	}  	return ret; @@ -298,7 +310,6 @@ int dbg_set_sw_break(unsigned long addr)  int dbg_deactivate_sw_breakpoints(void)  { -	unsigned long addr;  	int error;  	int ret = 0;  	int i; @@ -306,15 +317,14 @@ int dbg_deactivate_sw_breakpoints(void)  	for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {  		if (kgdb_break[i].state != BP_ACTIVE)  			continue; -		addr = kgdb_break[i].bpt_addr; -		error = kgdb_arch_remove_breakpoint(addr, -					kgdb_break[i].saved_instr); +		error = kgdb_arch_remove_breakpoint(&kgdb_break[i]);  		if (error) { -			printk(KERN_INFO "KGDB: BP remove failed: %lx\n", addr); +			printk(KERN_INFO "KGDB: BP remove failed: %lx\n", +			       kgdb_break[i].bpt_addr);  			ret = error;  		} -		kgdb_flush_swbreak_addr(addr); +		kgdb_flush_swbreak_addr(kgdb_break[i].bpt_addr);  		kgdb_break[i].state = BP_SET;  	}  	return ret; @@ -348,7 +358,6 @@ int kgdb_isremovedbreak(unsigned long addr)  int dbg_remove_all_break(void)  { -	unsigned long addr;  	int error;  	int i; @@ -356,12 +365,10 @@ int dbg_remove_all_break(void)  	for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {  		if (kgdb_break[i].state != BP_ACTIVE)  			goto setundefined; -		addr = kgdb_break[i].bpt_addr; -		error = kgdb_arch_remove_breakpoint(addr, -				kgdb_break[i].saved_instr); +		error = kgdb_arch_remove_breakpoint(&kgdb_break[i]);  		if (error)  			printk(KERN_ERR "KGDB: breakpoint remove failed: %lx\n", -			   addr); +			       kgdb_break[i].bpt_addr);  setundefined:  		kgdb_break[i].state = BP_UNDEFINED;  	} @@ -527,7 +534,7 @@ return_normal:  			kgdb_info[cpu].exception_state &=  				~(DCPU_WANT_MASTER | DCPU_IS_SLAVE);  			kgdb_info[cpu].enter_kgdb--; -			smp_mb__before_atomic_dec(); +			smp_mb__before_atomic();  			atomic_dec(&slaves_in_kgdb);  			dbg_touch_watchdogs();  			local_irq_restore(flags); @@ -538,7 +545,7 @@ return_normal:  	/*  	 * For single stepping, try to only enter on the processor -	 * that was single stepping.  To gaurd against a deadlock, the +	 * that was single stepping.  To guard against a deadlock, the  	 * kernel will only try for the value of sstep_tries before  	 * giving up and continuing on.  	 */ @@ -576,8 +583,12 @@ return_normal:  		raw_spin_lock(&dbg_slave_lock);  #ifdef CONFIG_SMP +	/* If send_ready set, slaves are already waiting */ +	if (ks->send_ready) +		atomic_set(ks->send_ready, 1); +  	/* Signal the other CPUs to enter kgdb_wait() */ -	if ((!kgdb_single_step) && kgdb_do_roundup) +	else if ((!kgdb_single_step) && kgdb_do_roundup)  		kgdb_roundup_cpus(flags);  #endif @@ -651,7 +662,7 @@ kgdb_restore:  	kgdb_info[cpu].exception_state &=  		~(DCPU_WANT_MASTER | DCPU_IS_SLAVE);  	kgdb_info[cpu].enter_kgdb--; -	smp_mb__before_atomic_dec(); +	smp_mb__before_atomic();  	atomic_dec(&masters_in_kgdb);  	/* Free kgdb_active */  	atomic_set(&kgdb_active, -1); @@ -674,22 +685,46 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)  {  	struct kgdb_state kgdb_var;  	struct kgdb_state *ks = &kgdb_var; +	int ret = 0; +	if (arch_kgdb_ops.enable_nmi) +		arch_kgdb_ops.enable_nmi(0); + +	memset(ks, 0, sizeof(struct kgdb_state));  	ks->cpu			= raw_smp_processor_id();  	ks->ex_vector		= evector;  	ks->signo		= signo;  	ks->err_code		= ecode; -	ks->kgdb_usethreadid	= 0;  	ks->linux_regs		= regs;  	if (kgdb_reenter_check(ks)) -		return 0; /* Ouch, double exception ! */ +		goto out; /* Ouch, double exception ! */  	if (kgdb_info[ks->cpu].enter_kgdb != 0) -		return 0; +		goto out; + +	ret = kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER); +out: +	if (arch_kgdb_ops.enable_nmi) +		arch_kgdb_ops.enable_nmi(1); +	return ret; +} -	return kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER); +/* + * GDB places a breakpoint at this function to know dynamically + * loaded objects. It's not defined static so that only one instance with this + * name exists in the kernel. + */ + +static int module_event(struct notifier_block *self, unsigned long val, +	void *data) +{ +	return 0;  } +static struct notifier_block dbg_module_load_nb = { +	.notifier_call	= module_event, +}; +  int kgdb_nmicallback(int cpu, void *regs)  {  #ifdef CONFIG_SMP @@ -709,6 +744,31 @@ int kgdb_nmicallback(int cpu, void *regs)  	return 1;  } +int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code, +							atomic_t *send_ready) +{ +#ifdef CONFIG_SMP +	if (!kgdb_io_ready(0) || !send_ready) +		return 1; + +	if (kgdb_info[cpu].enter_kgdb == 0) { +		struct kgdb_state kgdb_var; +		struct kgdb_state *ks = &kgdb_var; + +		memset(ks, 0, sizeof(struct kgdb_state)); +		ks->cpu			= cpu; +		ks->ex_vector		= trapnr; +		ks->signo		= SIGTRAP; +		ks->err_code		= err_code; +		ks->linux_regs		= regs; +		ks->send_ready		= send_ready; +		kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER); +		return 0; +	} +#endif +	return 1; +} +  static void kgdb_console_write(struct console *co, const char *s,     unsigned count)  { @@ -752,7 +812,7 @@ static void sysrq_handle_dbg(int key)  static struct sysrq_key_op sysrq_dbg_op = {  	.handler	= sysrq_handle_dbg, -	.help_msg	= "debug(G)", +	.help_msg	= "debug(g)",  	.action_msg	= "DEBUG",  };  #endif @@ -784,6 +844,33 @@ void __init dbg_late_init(void)  	kdb_init(KDB_INIT_FULL);  } +static int +dbg_notify_reboot(struct notifier_block *this, unsigned long code, void *x) +{ +	/* +	 * Take the following action on reboot notify depending on value: +	 *    1 == Enter debugger +	 *    0 == [the default] detatch debug client +	 *   -1 == Do nothing... and use this until the board resets +	 */ +	switch (kgdbreboot) { +	case 1: +		kgdb_breakpoint(); +	case -1: +		goto done; +	} +	if (!dbg_kdb_mode) +		gdbstub_exit(code); +done: +	return NOTIFY_DONE; +} + +static struct notifier_block dbg_reboot_notifier = { +	.notifier_call		= dbg_notify_reboot, +	.next			= NULL, +	.priority		= INT_MAX, +}; +  static void kgdb_register_callbacks(void)  {  	if (!kgdb_io_module_registered) { @@ -791,6 +878,8 @@ static void kgdb_register_callbacks(void)  		kgdb_arch_init();  		if (!dbg_is_early)  			kgdb_arch_late(); +		register_module_notifier(&dbg_module_load_nb); +		register_reboot_notifier(&dbg_reboot_notifier);  		atomic_notifier_chain_register(&panic_notifier_list,  					       &kgdb_panic_event_nb);  #ifdef CONFIG_MAGIC_SYSRQ @@ -812,6 +901,8 @@ static void kgdb_unregister_callbacks(void)  	 */  	if (kgdb_io_module_registered) {  		kgdb_io_module_registered = 0; +		unregister_reboot_notifier(&dbg_reboot_notifier); +		unregister_module_notifier(&dbg_module_load_nb);  		atomic_notifier_chain_unregister(&panic_notifier_list,  					       &kgdb_panic_event_nb);  		kgdb_arch_exit(); @@ -952,7 +1043,7 @@ int dbg_io_get_char(void)   * otherwise as a quick means to stop program execution and "break" into   * the debugger.   */ -void kgdb_breakpoint(void) +noinline void kgdb_breakpoint(void)  {  	atomic_inc(&kgdb_setting_breakpoint);  	wmb(); /* Sync point before breakpoint */  | 
