* to test the HW NMI watchdog
* F## = Break at do_fork for ## iterations
* S## = Break at sys_open for ## iterations
+ * I## = Run the single step test ## iterations
*
* NOTE: that the do_fork and sys_open tests are mutually exclusive.
*
#include <linux/nmi.h>
#include <linux/delay.h>
#include <linux/kthread.h>
-#include <linux/delay.h>
#define v1printk(a...) do { \
if (verbose) \
printk(KERN_INFO a); \
touch_nmi_watchdog(); \
} while (0)
+#define eprintk(a...) do { \
+ printk(KERN_ERR a); \
+ WARN_ON(1); \
+ } while (0)
#define MAX_CONFIG_LEN 40
-static const char hexchars[] = "0123456789abcdef";
static struct kgdb_io kgdbts_io_ops;
static char get_buf[BUFMAX];
static int get_buf_cnt;
static int test_complete;
static int send_ack;
static int final_ack;
+static int force_hwbrks;
+static int hwbreaks_ok;
static int hw_break_val;
static int hw_break_val2;
-#if defined(CONFIG_ARM) || defined(CONFIG_MIPS)
+#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC)
static int arch_needs_sstep_emulation = 1;
#else
static int arch_needs_sstep_emulation;
/* -1 = init not run yet, 0 = unconfigured, 1 = configured. */
static int configured = -1;
+#ifdef CONFIG_KGDB_TESTS_BOOT_STRING
+static char config[MAX_CONFIG_LEN] = CONFIG_KGDB_TESTS_BOOT_STRING;
+#else
static char config[MAX_CONFIG_LEN];
+#endif
static struct kparam_string kps = {
.string = config,
.maxlen = MAX_CONFIG_LEN,
static void sw_break(char *arg)
{
- break_helper("Z0", arg, 0);
+ break_helper(force_hwbrks ? "Z1" : "Z0", arg, 0);
}
static void sw_rem_break(char *arg)
{
- break_helper("z0", arg, 0);
+ break_helper(force_hwbrks ? "z1" : "z0", arg, 0);
}
static void hw_break(char *arg)
#endif
if (strcmp(arg, "silent") &&
instruction_pointer(&kgdbts_regs) + offset != addr) {
- printk(KERN_ERR "kgdbts: BP mismatch %lx expected %lx\n",
+ eprintk("kgdbts: BP mismatch %lx expected %lx\n",
instruction_pointer(&kgdbts_regs) + offset, addr);
return 1;
}
v2printk("Singlestep stopped at IP: %lx\n",
instruction_pointer(&kgdbts_regs));
if (instruction_pointer(&kgdbts_regs) == addr) {
- printk(KERN_ERR "kgdbts: SingleStep failed at %lx\n",
+ eprintk("kgdbts: SingleStep failed at %lx\n",
instruction_pointer(&kgdbts_regs));
return 1;
}
break;
case 1:
/* set breakpoint */
- break_helper("Z0", 0, sstep_addr);
+ break_helper("Z0", NULL, sstep_addr);
break;
case 2:
/* Continue */
break;
case 3:
/* Clear breakpoint */
- break_helper("z0", 0, sstep_addr);
+ break_helper("z0", NULL, sstep_addr);
break;
default:
- printk(KERN_ERR "kgdbts: ERROR failed sstep get emulation\n");
+ eprintk("kgdbts: ERROR failed sstep get emulation\n");
}
sstep_state++;
}
break;
case 2:
if (strncmp(put_str, "$OK", 3)) {
- printk(KERN_ERR "kgdbts: failed sstep break set\n");
+ eprintk("kgdbts: failed sstep break set\n");
return 1;
}
break;
case 3:
if (strncmp(put_str, "$T0", 3)) {
- printk(KERN_ERR "kgdbts: failed continue sstep\n");
+ eprintk("kgdbts: failed continue sstep\n");
return 1;
}
break;
case 4:
if (strncmp(put_str, "$OK", 3)) {
- printk(KERN_ERR "kgdbts: failed sstep break unset\n");
+ eprintk("kgdbts: failed sstep break unset\n");
return 1;
}
/* Single step is complete so continue on! */
sstep_state = 0;
return 0;
default:
- printk(KERN_ERR "kgdbts: ERROR failed sstep put emulation\n");
+ eprintk("kgdbts: ERROR failed sstep put emulation\n");
}
/* Continue on the same test line until emulation is complete */
{ "?", "S0*" }, /* Clear break points */
{ "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
{ "c", "T0*", }, /* Continue */
- { "g", "kgdbts_break_test", 0, check_and_rewind_pc },
+ { "g", "kgdbts_break_test", NULL, check_and_rewind_pc },
{ "write", "OK", write_regs },
{ "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */
{ "D", "OK" }, /* Detach */
- { "D", "OK", 0, got_break }, /* If the test worked we made it here */
+ { "D", "OK", NULL, got_break }, /* On success we made it here */
{ "", "" },
};
{ "?", "S0*" }, /* Clear break points */
{ "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
{ "c", "T0*", }, /* Continue */
- { "g", "kgdbts_break_test", 0, check_and_rewind_pc },
+ { "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", 0, check_single_step },
+ { "g", "kgdbts_break_test", NULL, check_single_step },
{ "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
{ "c", "T0*", }, /* Continue */
- { "g", "kgdbts_break_test", 0, check_and_rewind_pc },
+ { "g", "kgdbts_break_test", NULL, check_and_rewind_pc },
{ "write", "OK", write_regs }, /* Write registers */
{ "D", "OK" }, /* Remove all breakpoints and continues */
{ "", "" },
{ "?", "S0*" }, /* Clear break points */
{ "do_fork", "OK", sw_break, }, /* set sw breakpoint */
{ "c", "T0*", }, /* Continue */
- { "g", "do_fork", 0, check_and_rewind_pc }, /* check location */
+ { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */
{ "write", "OK", write_regs }, /* Write registers */
{ "do_fork", "OK", sw_rem_break }, /*remove breakpoint */
{ "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
- { "g", "do_fork", 0, check_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", 0, final_ack_set }, /* detach and unregister I/O */
+ { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */
{ "", "" },
};
{ "?", "S0*" }, /* Clear break points */
{ "sys_open", "OK", sw_break, }, /* set sw breakpoint */
{ "c", "T0*", }, /* Continue */
- { "g", "sys_open", 0, check_and_rewind_pc }, /* check location */
+ { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */
{ "write", "OK", write_regs }, /* Write registers */
{ "sys_open", "OK", sw_rem_break }, /*remove breakpoint */
{ "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
- { "g", "sys_open", 0, check_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", 0, final_ack_set }, /* detach and unregister I/O */
+ { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */
{ "", "" },
};
{ "?", "S0*" }, /* Clear break points */
{ "kgdbts_break_test", "OK", hw_break, }, /* set hw breakpoint */
{ "c", "T0*", }, /* Continue */
- { "g", "kgdbts_break_test", 0, check_and_rewind_pc },
+ { "g", "kgdbts_break_test", NULL, check_and_rewind_pc },
{ "write", "OK", write_regs },
{ "kgdbts_break_test", "OK", hw_rem_break }, /*remove breakpoint */
{ "D", "OK" }, /* Detach */
- { "D", "OK", 0, got_break }, /* If the test worked we made it here */
+ { "D", "OK", NULL, got_break }, /* On success we made it here */
{ "", "" },
};
static struct test_struct hw_write_break_test[] = {
{ "?", "S0*" }, /* Clear break points */
{ "hw_break_val", "OK", hw_write_break, }, /* set hw breakpoint */
- { "c", "T0*", 0, got_break }, /* Continue */
- { "g", "silent", 0, check_and_rewind_pc },
+ { "c", "T0*", NULL, got_break }, /* Continue */
+ { "g", "silent", NULL, check_and_rewind_pc },
{ "write", "OK", write_regs },
{ "hw_break_val", "OK", hw_rem_write_break }, /*remove breakpoint */
{ "D", "OK" }, /* Detach */
- { "D", "OK", 0, got_break }, /* If the test worked we made it here */
+ { "D", "OK", NULL, got_break }, /* On success we made it here */
{ "", "" },
};
static struct test_struct hw_access_break_test[] = {
{ "?", "S0*" }, /* Clear break points */
{ "hw_break_val", "OK", hw_access_break, }, /* set hw breakpoint */
- { "c", "T0*", 0, got_break }, /* Continue */
- { "g", "silent", 0, check_and_rewind_pc },
+ { "c", "T0*", NULL, got_break }, /* Continue */
+ { "g", "silent", NULL, check_and_rewind_pc },
{ "write", "OK", write_regs },
{ "hw_break_val", "OK", hw_rem_access_break }, /*remove breakpoint */
{ "D", "OK" }, /* Detach */
- { "D", "OK", 0, got_break }, /* If the test worked we made it here */
+ { "D", "OK", NULL, got_break }, /* On success we made it here */
{ "", "" },
};
*/
static struct test_struct nmi_sleep_test[] = {
{ "?", "S0*" }, /* Clear break points */
- { "c", "T0*", 0, got_break }, /* Continue */
+ { "c", "T0*", NULL, got_break }, /* Continue */
{ "D", "OK" }, /* Detach */
- { "D", "OK", 0, got_break }, /* If the test worked we made it here */
+ { "D", "OK", NULL, got_break }, /* On success we made it here */
{ "", "" },
};
count++;
}
strcat(get_buf, "#");
- get_buf[count + 2] = hexchars[checksum >> 4];
- get_buf[count + 3] = hexchars[checksum & 0xf];
+ get_buf[count + 2] = hex_asc_hi(checksum);
+ get_buf[count + 3] = hex_asc_lo(checksum);
get_buf[count + 4] = '\0';
v2printk("get%i: %s\n", ts.idx, get_buf);
}
}
if (get_buf[get_buf_cnt] == '\0') {
- printk(KERN_ERR
- "kgdbts: ERROR GET: end of buffer on '%s' at %i\n",
+ eprintk("kgdbts: ERROR GET: EOB on '%s' at %i\n",
ts.name, ts.idx);
get_buf_cnt = 0;
fill_get_buf("D");
*/
if (ts.tst[ts.idx].get[0] == '\0' &&
ts.tst[ts.idx].put[0] == '\0') {
- printk(KERN_ERR "kgdbts: ERROR: beyond end of test on"
+ eprintk("kgdbts: ERROR: beyond end of test on"
" '%s' line %i\n", ts.name, ts.idx);
return 0;
}
if (put_buf_cnt >= BUFMAX) {
- printk(KERN_ERR "kgdbts: ERROR: put buffer overflow on"
+ eprintk("kgdbts: ERROR: put buffer overflow on"
" '%s' line %i\n", ts.name, ts.idx);
put_buf_cnt = 0;
return 0;
/* End of packet == #XX so look for the '#' */
if (put_buf_cnt > 3 && put_buf[put_buf_cnt - 3] == '#') {
+ if (put_buf_cnt >= BUFMAX) {
+ eprintk("kgdbts: ERROR: put buffer overflow on"
+ " '%s' line %i\n", ts.name, ts.idx);
+ put_buf_cnt = 0;
+ return 0;
+ }
put_buf[put_buf_cnt] = '\0';
v2printk("put%i: %s\n", ts.idx, put_buf);
/* Trigger check here */
if (ts.validate_put && ts.validate_put(put_buf)) {
- printk(KERN_ERR "kgdbts: ERROR PUT: end of test "
+ eprintk("kgdbts: ERROR PUT: end of test "
"buffer on '%s' line %i expected %s got %s\n",
ts.name, ts.idx, ts.tst[ts.idx].put, put_buf);
}
if (test_complete)
return;
- printk(KERN_ERR "kgdbts: ERROR %s test failed\n", ts.name);
+ eprintk("kgdbts: ERROR %s test failed\n", ts.name);
+ if (is_hw_breakpoint)
+ hwbreaks_ok = 0;
}
static void run_hw_break_test(int is_write_test)
kgdb_breakpoint();
hw_break_val_access();
if (is_write_test) {
- if (test_complete == 2)
- printk(KERN_ERR "kgdbts: ERROR %s broke on access\n",
+ if (test_complete == 2) {
+ eprintk("kgdbts: ERROR %s broke on access\n",
ts.name);
+ hwbreaks_ok = 0;
+ }
hw_break_val_write();
}
kgdb_breakpoint();
if (test_complete == 1)
return;
- printk(KERN_ERR "kgdbts: ERROR %s test failed\n", ts.name);
+ eprintk("kgdbts: ERROR %s test failed\n", ts.name);
+ hwbreaks_ok = 0;
}
static void run_nmi_sleep_test(int nmi_sleep)
touch_nmi_watchdog();
local_irq_restore(flags);
if (test_complete != 2)
- printk(KERN_ERR "kgdbts: ERROR nmi_test did not hit nmi\n");
+ eprintk("kgdbts: ERROR nmi_test did not hit nmi\n");
kgdb_breakpoint();
if (test_complete == 1)
return;
- printk(KERN_ERR "kgdbts: ERROR %s test failed\n", ts.name);
+ eprintk("kgdbts: ERROR %s test failed\n", ts.name);
}
static void run_bad_read_test(void)
{
char *ptr;
int fork_test = 0;
- int sys_open_test = 0;
+ int do_sys_open_test = 0;
+ int sstep_test = 1000;
int nmi_sleep = 0;
+ int i;
- ptr = strstr(config, "F");
+ ptr = strchr(config, 'F');
if (ptr)
- fork_test = simple_strtol(ptr+1, NULL, 10);
- ptr = strstr(config, "S");
+ fork_test = simple_strtol(ptr + 1, NULL, 10);
+ ptr = strchr(config, 'S');
if (ptr)
- sys_open_test = simple_strtol(ptr+1, NULL, 10);
- ptr = strstr(config, "N");
+ do_sys_open_test = simple_strtol(ptr + 1, NULL, 10);
+ ptr = strchr(config, 'N');
if (ptr)
nmi_sleep = simple_strtol(ptr+1, NULL, 10);
+ ptr = strchr(config, 'I');
+ if (ptr)
+ sstep_test = simple_strtol(ptr+1, NULL, 10);
/* required internal KGDB tests */
v1printk("kgdbts:RUN plant and detach test\n");
run_breakpoint_test(0);
v1printk("kgdbts:RUN bad memory access test\n");
run_bad_read_test();
- v1printk("kgdbts:RUN singlestep breakpoint test\n");
- run_singlestep_break_test();
+ v1printk("kgdbts:RUN singlestep test %i iterations\n", sstep_test);
+ for (i = 0; i < sstep_test; i++) {
+ run_singlestep_break_test();
+ if (i % 100 == 0)
+ v1printk("kgdbts:RUN singlestep [%i/%i]\n",
+ i, sstep_test);
+ }
/* ===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_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.
repeat_test = fork_test;
printk(KERN_INFO "kgdbts:RUN do_fork for %i breakpoints\n",
repeat_test);
- kthread_run(kgdbts_unreg_thread, 0, "kgdbts_unreg");
+ kthread_run(kgdbts_unreg_thread, NULL, "kgdbts_unreg");
run_do_fork_test();
return;
}
* executed because a kernel thread will be spawned at the very
* end to unregister the debug hooks.
*/
- if (sys_open_test) {
- repeat_test = sys_open_test;
+ if (do_sys_open_test) {
+ repeat_test = do_sys_open_test;
printk(KERN_INFO "kgdbts:RUN sys_open for %i breakpoints\n",
repeat_test);
- kthread_run(kgdbts_unreg_thread, 0, "kgdbts_unreg");
+ kthread_run(kgdbts_unreg_thread, NULL, "kgdbts_unreg");
run_sys_open_test();
return;
}