diff options
Diffstat (limited to 'arch/x86/kernel/kgdb.c')
| -rw-r--r-- | arch/x86/kernel/kgdb.c | 60 | 
1 files changed, 60 insertions, 0 deletions
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index db6720edfdd..8bfb6146f75 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c @@ -43,6 +43,8 @@  #include <linux/smp.h>  #include <linux/nmi.h>  #include <linux/hw_breakpoint.h> +#include <linux/uaccess.h> +#include <linux/memory.h>  #include <asm/debugreg.h>  #include <asm/apicdef.h> @@ -741,6 +743,64 @@ void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)  	regs->ip = ip;  } +int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) +{ +	int err; +	char opc[BREAK_INSTR_SIZE]; + +	bpt->type = BP_BREAKPOINT; +	err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr, +				BREAK_INSTR_SIZE); +	if (err) +		return err; +	err = probe_kernel_write((char *)bpt->bpt_addr, +				 arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE); +#ifdef CONFIG_DEBUG_RODATA +	if (!err) +		return err; +	/* +	 * It is safe to call text_poke() because normal kernel execution +	 * is stopped on all cores, so long as the text_mutex is not locked. +	 */ +	if (mutex_is_locked(&text_mutex)) +		return -EBUSY; +	text_poke((void *)bpt->bpt_addr, arch_kgdb_ops.gdb_bpt_instr, +		  BREAK_INSTR_SIZE); +	err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE); +	if (err) +		return err; +	if (memcmp(opc, arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE)) +		return -EINVAL; +	bpt->type = BP_POKE_BREAKPOINT; +#endif /* CONFIG_DEBUG_RODATA */ +	return err; +} + +int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) +{ +#ifdef CONFIG_DEBUG_RODATA +	int err; +	char opc[BREAK_INSTR_SIZE]; + +	if (bpt->type != BP_POKE_BREAKPOINT) +		goto knl_write; +	/* +	 * It is safe to call text_poke() because normal kernel execution +	 * is stopped on all cores, so long as the text_mutex is not locked. +	 */ +	if (mutex_is_locked(&text_mutex)) +		goto knl_write; +	text_poke((void *)bpt->bpt_addr, bpt->saved_instr, BREAK_INSTR_SIZE); +	err = probe_kernel_read(opc, (char *)bpt->bpt_addr, BREAK_INSTR_SIZE); +	if (err || memcmp(opc, bpt->saved_instr, BREAK_INSTR_SIZE)) +		goto knl_write; +	return err; +knl_write: +#endif /* CONFIG_DEBUG_RODATA */ +	return probe_kernel_write((char *)bpt->bpt_addr, +				  (char *)bpt->saved_instr, BREAK_INSTR_SIZE); +} +  struct kgdb_arch arch_kgdb_ops = {  	/* Breakpoint instruction: */  	.gdb_bpt_instr		= { 0xcc },  |