Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c216ec6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull KGDB/KDB regression fixes from Jason Wessel:
 - Fix a Smatch warning that appeared in the 3.4 merge window
 - Fix kgdb test suite with SMP for all archs without HW single stepping
 - Fix kgdb sw breakpoints with CONFIG_DEBUG_RODATA=y limitations on x86
 - Fix oops on kgdb test suite with CONFIG_DEBUG_RODATA
 - Fix kgdb test suite with SMP for all archs with HW single stepping

* tag 'for_linus-3.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/kgdb:
  x86,kgdb: Fix DEBUG_RODATA limitation using text_poke()
  kgdb,debug_core: pass the breakpoint struct instead of address and memory
  kgdbts: (2 of 2) fix single step awareness to work correctly with SMP
  kgdbts: (1 of 2) fix single step awareness to work correctly with SMP
  kgdbts: Fix kernel oops with CONFIG_DEBUG_RODATA
  kdb: Fix smatch warning on dbg_io_ops->is_console
parents 58bca4a8 3751d3e8
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -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 },
+115 −45
Original line number Diff line number Diff line
@@ -134,12 +134,17 @@ static int force_hwbrks;
static int hwbreaks_ok;
static int hw_break_val;
static int hw_break_val2;
static int cont_instead_of_sstep;
static unsigned long cont_thread_id;
static unsigned long sstep_thread_id;
#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC)
static int arch_needs_sstep_emulation = 1;
#else
static int arch_needs_sstep_emulation;
#endif
static unsigned long cont_addr;
static unsigned long sstep_addr;
static int restart_from_top_after_write;
static int sstep_state;

/* Storage for the registers, in GDB format. */
@@ -187,7 +192,8 @@ static int kgdbts_unreg_thread(void *ptr)
	 */
	while (!final_ack)
		msleep_interruptible(1500);

	/* Pause for any other threads to exit after final ack. */
	msleep_interruptible(1000);
	if (configured)
		kgdb_unregister_io_module(&kgdbts_io_ops);
	configured = 0;
@@ -211,7 +217,7 @@ static unsigned long lookup_addr(char *arg)
	if (!strcmp(arg, "kgdbts_break_test"))
		addr = (unsigned long)kgdbts_break_test;
	else if (!strcmp(arg, "sys_open"))
		addr = (unsigned long)sys_open;
		addr = (unsigned long)do_sys_open;
	else if (!strcmp(arg, "do_fork"))
		addr = (unsigned long)do_fork;
	else if (!strcmp(arg, "hw_break_val"))
@@ -283,6 +289,16 @@ static void hw_break_val_write(void)
	hw_break_val++;
}

static int get_thread_id_continue(char *put_str, char *arg)
{
	char *ptr = &put_str[11];

	if (put_str[1] != 'T' || put_str[2] != '0')
		return 1;
	kgdb_hex2long(&ptr, &cont_thread_id);
	return 0;
}

static int check_and_rewind_pc(char *put_str, char *arg)
{
	unsigned long addr = lookup_addr(arg);
@@ -299,13 +315,21 @@ static int check_and_rewind_pc(char *put_str, char *arg)
	if (addr + BREAK_INSTR_SIZE == ip)
		offset = -BREAK_INSTR_SIZE;
#endif
	if (strcmp(arg, "silent") && ip + offset != addr) {

	if (arch_needs_sstep_emulation && sstep_addr &&
	    ip + offset == sstep_addr &&
	    ((!strcmp(arg, "sys_open") || !strcmp(arg, "do_fork")))) {
		/* This is special case for emulated single step */
		v2printk("Emul: rewind hit single step bp\n");
		restart_from_top_after_write = 1;
	} else if (strcmp(arg, "silent") && ip + offset != addr) {
		eprintk("kgdbts: BP mismatch %lx expected %lx\n",
			   ip + offset, addr);
		return 1;
	}
	/* Readjust the instruction pointer if needed */
	ip += offset;
	cont_addr = ip;
#ifdef GDB_ADJUSTS_BREAK_OFFSET
	instruction_pointer_set(&kgdbts_regs, ip);
#endif
@@ -315,6 +339,8 @@ static int check_and_rewind_pc(char *put_str, char *arg)
static int check_single_step(char *put_str, char *arg)
{
	unsigned long addr = lookup_addr(arg);
	static int matched_id;

	/*
	 * From an arch indepent point of view the instruction pointer
	 * should be on a different instruction
@@ -324,6 +350,29 @@ static int check_single_step(char *put_str, char *arg)
	gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs);
	v2printk("Singlestep stopped at IP: %lx\n",
		   instruction_pointer(&kgdbts_regs));

	if (sstep_thread_id != cont_thread_id) {
		/*
		 * Ensure we stopped in the same thread id as before, else the
		 * debugger should continue until the original thread that was
		 * single stepped is scheduled again, emulating gdb's behavior.
		 */
		v2printk("ThrID does not match: %lx\n", cont_thread_id);
		if (arch_needs_sstep_emulation) {
			if (matched_id &&
			    instruction_pointer(&kgdbts_regs) != addr)
				goto continue_test;
			matched_id++;
			ts.idx -= 2;
			sstep_state = 0;
			return 0;
		}
		cont_instead_of_sstep = 1;
		ts.idx -= 4;
		return 0;
	}
continue_test:
	matched_id = 0;
	if (instruction_pointer(&kgdbts_regs) == addr) {
		eprintk("kgdbts: SingleStep failed at %lx\n",
			   instruction_pointer(&kgdbts_regs));
@@ -365,10 +414,40 @@ static int got_break(char *put_str, char *arg)
	return 1;
}

static void get_cont_catch(char *arg)
{
	/* Always send detach because the test is completed at this point */
	fill_get_buf("D");
}

static int put_cont_catch(char *put_str, char *arg)
{
	/* This is at the end of the test and we catch any and all input */
	v2printk("kgdbts: cleanup task: %lx\n", sstep_thread_id);
	ts.idx--;
	return 0;
}

static int emul_reset(char *put_str, char *arg)
{
	if (strncmp(put_str, "$OK", 3))
		return 1;
	if (restart_from_top_after_write) {
		restart_from_top_after_write = 0;
		ts.idx = -1;
	}
	return 0;
}

static void emul_sstep_get(char *arg)
{
	if (!arch_needs_sstep_emulation) {
		if (cont_instead_of_sstep) {
			cont_instead_of_sstep = 0;
			fill_get_buf("c");
		} else {
			fill_get_buf(arg);
		}
		return;
	}
	switch (sstep_state) {
@@ -398,9 +477,11 @@ static void emul_sstep_get(char *arg)
static int emul_sstep_put(char *put_str, char *arg)
{
	if (!arch_needs_sstep_emulation) {
		if (!strncmp(put_str+1, arg, 2))
			return 0;
		char *ptr = &put_str[11];
		if (put_str[1] != 'T' || put_str[2] != '0')
			return 1;
		kgdb_hex2long(&ptr, &sstep_thread_id);
		return 0;
	}
	switch (sstep_state) {
	case 1:
@@ -411,8 +492,7 @@ static int emul_sstep_put(char *put_str, char *arg)
		v2printk("Stopped at IP: %lx\n",
			 instruction_pointer(&kgdbts_regs));
		/* Want to stop at IP + break instruction size by default */
		sstep_addr = instruction_pointer(&kgdbts_regs) +
			BREAK_INSTR_SIZE;
		sstep_addr = cont_addr + BREAK_INSTR_SIZE;
		break;
	case 2:
		if (strncmp(put_str, "$OK", 3)) {
@@ -424,6 +504,9 @@ static int emul_sstep_put(char *put_str, char *arg)
		if (strncmp(put_str, "$T0", 3)) {
			eprintk("kgdbts: failed continue sstep\n");
			return 1;
		} else {
			char *ptr = &put_str[11];
			kgdb_hex2long(&ptr, &sstep_thread_id);
		}
		break;
	case 4:
@@ -502,10 +585,10 @@ static struct test_struct bad_read_test[] = {
static struct test_struct singlestep_break_test[] = {
	{ "?", "S0*" }, /* Clear break points */
	{ "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
	{ "c", "T0*", }, /* Continue */
	{ "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
	{ "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */
	{ "g", "kgdbts_break_test", NULL, check_and_rewind_pc },
	{ "write", "OK", write_regs }, /* Write registers */
	{ "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */
	{ "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
	{ "g", "kgdbts_break_test", NULL, check_single_step },
	{ "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
@@ -523,16 +606,16 @@ static struct test_struct singlestep_break_test[] = {
static struct test_struct do_fork_test[] = {
	{ "?", "S0*" }, /* Clear break points */
	{ "do_fork", "OK", sw_break, }, /* set sw breakpoint */
	{ "c", "T0*", }, /* Continue */
	{ "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */
	{ "write", "OK", write_regs }, /* Write registers */
	{ "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
	{ "do_fork", "OK", sw_rem_break }, /*remove breakpoint */
	{ "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */
	{ "write", "OK", write_regs, emul_reset }, /* Write registers */
	{ "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
	{ "g", "do_fork", NULL, check_single_step },
	{ "do_fork", "OK", sw_break, }, /* set sw breakpoint */
	{ "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */
	{ "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */
	{ "", "" },
	{ "", "", get_cont_catch, put_cont_catch },
};

/* Test for hitting a breakpoint at sys_open for what ever the number
@@ -541,16 +624,16 @@ static struct test_struct do_fork_test[] = {
static struct test_struct sys_open_test[] = {
	{ "?", "S0*" }, /* Clear break points */
	{ "sys_open", "OK", sw_break, }, /* set sw breakpoint */
	{ "c", "T0*", }, /* Continue */
	{ "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */
	{ "write", "OK", write_regs }, /* Write registers */
	{ "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
	{ "sys_open", "OK", sw_rem_break }, /*remove breakpoint */
	{ "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */
	{ "write", "OK", write_regs, emul_reset }, /* Write registers */
	{ "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
	{ "g", "sys_open", NULL, check_single_step },
	{ "sys_open", "OK", sw_break, }, /* set sw breakpoint */
	{ "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */
	{ "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */
	{ "", "" },
	{ "", "", get_cont_catch, put_cont_catch },
};

/*
@@ -693,8 +776,8 @@ static int run_simple_test(int is_get_char, int chr)
	/* This callback is a put char which is when kgdb sends data to
	 * this I/O module.
	 */
	if (ts.tst[ts.idx].get[0] == '\0' &&
		ts.tst[ts.idx].put[0] == '\0') {
	if (ts.tst[ts.idx].get[0] == '\0' && ts.tst[ts.idx].put[0] == '\0' &&
	    !ts.tst[ts.idx].get_handler) {
		eprintk("kgdbts: ERROR: beyond end of test on"
			   " '%s' line %i\n", ts.name, ts.idx);
		return 0;
@@ -907,6 +990,17 @@ static void kgdbts_run_tests(void)
	if (ptr)
		sstep_test = simple_strtol(ptr+1, NULL, 10);

	/* All HW break point tests */
	if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) {
		hwbreaks_ok = 1;
		v1printk("kgdbts:RUN hw breakpoint test\n");
		run_breakpoint_test(1);
		v1printk("kgdbts:RUN hw write breakpoint test\n");
		run_hw_break_test(1);
		v1printk("kgdbts:RUN access write breakpoint test\n");
		run_hw_break_test(0);
	}

	/* required internal KGDB tests */
	v1printk("kgdbts:RUN plant and detach test\n");
	run_plant_and_detach_test(0);
@@ -924,35 +1018,11 @@ static void kgdbts_run_tests(void)

	/* ===Optional tests=== */

	/* All HW break point tests */
	if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) {
		hwbreaks_ok = 1;
		v1printk("kgdbts:RUN hw breakpoint test\n");
		run_breakpoint_test(1);
		v1printk("kgdbts:RUN hw write breakpoint test\n");
		run_hw_break_test(1);
		v1printk("kgdbts:RUN access write breakpoint test\n");
		run_hw_break_test(0);
	}

	if (nmi_sleep) {
		v1printk("kgdbts:RUN NMI sleep %i seconds test\n", nmi_sleep);
		run_nmi_sleep_test(nmi_sleep);
	}

#ifdef CONFIG_DEBUG_RODATA
	/* Until there is an api to write to read-only text segments, use
	 * HW breakpoints for the remainder of any tests, else print a
	 * failure message if hw breakpoints do not work.
	 */
	if (!(arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT && hwbreaks_ok)) {
		eprintk("kgdbts: HW breakpoints do not work,"
			"skipping remaining tests\n");
		return;
	}
	force_hwbrks = 1;
#endif /* CONFIG_DEBUG_RODATA */

	/* If the do_fork test is run it will be the last test that is
	 * executed because a kernel thread will be spawned at the very
	 * end to unregister the debug hooks.
+4 −3
Original line number Diff line number Diff line
@@ -63,7 +63,8 @@ enum kgdb_bptype {
	BP_HARDWARE_BREAKPOINT,
	BP_WRITE_WATCHPOINT,
	BP_READ_WATCHPOINT,
	BP_ACCESS_WATCHPOINT
	BP_ACCESS_WATCHPOINT,
	BP_POKE_BREAKPOINT,
};

enum kgdb_bpstate {
@@ -207,8 +208,8 @@ extern void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc);

/* Optional functions. */
extern int kgdb_validate_break_address(unsigned long addr);
extern int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr);
extern int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle);
extern int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt);
extern int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt);

/**
 *	kgdb_arch_late - Perform any architecture specific initalization.
+24 −29
Original line number Diff line number Diff line
@@ -160,37 +160,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);
@@ -234,7 +236,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;
@@ -243,16 +244,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;
@@ -301,7 +301,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;
@@ -309,15 +308,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;
@@ -351,7 +349,6 @@ int kgdb_isremovedbreak(unsigned long addr)

int dbg_remove_all_break(void)
{
	unsigned long addr;
	int error;
	int i;

@@ -359,12 +356,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;
	}
+1 −1
Original line number Diff line number Diff line
@@ -743,7 +743,7 @@ kdb_printit:
		kdb_input_flush();
		c = console_drivers;

		if (!dbg_io_ops->is_console) {
		if (dbg_io_ops && !dbg_io_ops->is_console) {
			len = strlen(moreprompt);
			cp = moreprompt;
			while (len--) {