| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1 | /* | 
 | 2 |  * kgdbts is a test suite for kgdb for the sole purpose of validating | 
 | 3 |  * that key pieces of the kgdb internals are working properly such as | 
 | 4 |  * HW/SW breakpoints, single stepping, and NMI. | 
 | 5 |  * | 
 | 6 |  * Created by: Jason Wessel <jason.wessel@windriver.com> | 
 | 7 |  * | 
 | 8 |  * Copyright (c) 2008 Wind River Systems, Inc. | 
 | 9 |  * | 
 | 10 |  * This program is free software; you can redistribute it and/or modify | 
 | 11 |  * it under the terms of the GNU General Public License version 2 as | 
 | 12 |  * published by the Free Software Foundation. | 
 | 13 |  * | 
 | 14 |  * This program is distributed in the hope that it will be useful, | 
 | 15 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 16 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 
 | 17 |  * See the GNU General Public License for more details. | 
 | 18 |  * | 
 | 19 |  * You should have received a copy of the GNU General Public License | 
 | 20 |  * along with this program; if not, write to the Free Software | 
 | 21 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
 | 22 |  */ | 
 | 23 | /* Information about the kgdb test suite. | 
 | 24 |  * ------------------------------------- | 
 | 25 |  * | 
 | 26 |  * The kgdb test suite is designed as a KGDB I/O module which | 
 | 27 |  * simulates the communications that a debugger would have with kgdb. | 
 | 28 |  * The tests are broken up in to a line by line and referenced here as | 
 | 29 |  * a "get" which is kgdb requesting input and "put" which is kgdb | 
 | 30 |  * sending a response. | 
 | 31 |  * | 
 | 32 |  * The kgdb suite can be invoked from the kernel command line | 
 | 33 |  * arguments system or executed dynamically at run time.  The test | 
 | 34 |  * suite uses the variable "kgdbts" to obtain the information about | 
 | 35 |  * which tests to run and to configure the verbosity level.  The | 
 | 36 |  * following are the various characters you can use with the kgdbts= | 
 | 37 |  * line: | 
 | 38 |  * | 
 | 39 |  * When using the "kgdbts=" you only choose one of the following core | 
 | 40 |  * test types: | 
 | 41 |  * A = Run all the core tests silently | 
 | 42 |  * V1 = Run all the core tests with minimal output | 
 | 43 |  * V2 = Run all the core tests in debug mode | 
 | 44 |  * | 
 | 45 |  * You can also specify optional tests: | 
 | 46 |  * N## = Go to sleep with interrupts of for ## seconds | 
 | 47 |  *       to test the HW NMI watchdog | 
 | 48 |  * F## = Break at do_fork for ## iterations | 
 | 49 |  * S## = Break at sys_open for ## iterations | 
| Jason Wessel | 7cfcd98 | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 50 |  * I## = Run the single step test ## iterations | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 51 |  * | 
 | 52 |  * NOTE: that the do_fork and sys_open tests are mutually exclusive. | 
 | 53 |  * | 
 | 54 |  * To invoke the kgdb test suite from boot you use a kernel start | 
 | 55 |  * argument as follows: | 
 | 56 |  * 	kgdbts=V1 kgdbwait | 
 | 57 |  * Or if you wanted to perform the NMI test for 6 seconds and do_fork | 
 | 58 |  * test for 100 forks, you could use: | 
 | 59 |  * 	kgdbts=V1N6F100 kgdbwait | 
 | 60 |  * | 
 | 61 |  * The test suite can also be invoked at run time with: | 
 | 62 |  *	echo kgdbts=V1N6F100 > /sys/module/kgdbts/parameters/kgdbts | 
 | 63 |  * Or as another example: | 
 | 64 |  *	echo kgdbts=V2 > /sys/module/kgdbts/parameters/kgdbts | 
 | 65 |  * | 
 | 66 |  * When developing a new kgdb arch specific implementation or | 
 | 67 |  * using these tests for the purpose of regression testing, | 
 | 68 |  * several invocations are required. | 
 | 69 |  * | 
 | 70 |  * 1) Boot with the test suite enabled by using the kernel arguments | 
 | 71 |  *       "kgdbts=V1F100 kgdbwait" | 
 | 72 |  *    ## If kgdb arch specific implementation has NMI use | 
 | 73 |  *       "kgdbts=V1N6F100 | 
 | 74 |  * | 
 | 75 |  * 2) After the system boot run the basic test. | 
 | 76 |  * echo kgdbts=V1 > /sys/module/kgdbts/parameters/kgdbts | 
 | 77 |  * | 
 | 78 |  * 3) Run the concurrency tests.  It is best to use n+1 | 
 | 79 |  *    while loops where n is the number of cpus you have | 
 | 80 |  *    in your system.  The example below uses only two | 
 | 81 |  *    loops. | 
 | 82 |  * | 
 | 83 |  * ## This tests break points on sys_open | 
 | 84 |  * while [ 1 ] ; do find / > /dev/null 2>&1 ; done & | 
 | 85 |  * while [ 1 ] ; do find / > /dev/null 2>&1 ; done & | 
 | 86 |  * echo kgdbts=V1S10000 > /sys/module/kgdbts/parameters/kgdbts | 
 | 87 |  * fg # and hit control-c | 
 | 88 |  * fg # and hit control-c | 
 | 89 |  * ## This tests break points on do_fork | 
 | 90 |  * while [ 1 ] ; do date > /dev/null ; done & | 
 | 91 |  * while [ 1 ] ; do date > /dev/null ; done & | 
 | 92 |  * echo kgdbts=V1F1000 > /sys/module/kgdbts/parameters/kgdbts | 
 | 93 |  * fg # and hit control-c | 
 | 94 |  * | 
 | 95 |  */ | 
 | 96 |  | 
 | 97 | #include <linux/kernel.h> | 
 | 98 | #include <linux/kgdb.h> | 
 | 99 | #include <linux/ctype.h> | 
 | 100 | #include <linux/uaccess.h> | 
 | 101 | #include <linux/syscalls.h> | 
 | 102 | #include <linux/nmi.h> | 
 | 103 | #include <linux/delay.h> | 
 | 104 | #include <linux/kthread.h> | 
| Paul Gortmaker | eb12a67 | 2011-07-03 15:14:56 -0400 | [diff] [blame] | 105 | #include <linux/module.h> | 
| Tiejun Chen | e78acf6 | 2013-02-27 11:09:27 +0800 | [diff] [blame] | 106 | #include <asm/sections.h> | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 107 |  | 
 | 108 | #define v1printk(a...) do { \ | 
 | 109 | 	if (verbose) \ | 
 | 110 | 		printk(KERN_INFO a); \ | 
 | 111 | 	} while (0) | 
 | 112 | #define v2printk(a...) do { \ | 
 | 113 | 	if (verbose > 1) \ | 
 | 114 | 		printk(KERN_INFO a); \ | 
 | 115 | 		touch_nmi_watchdog();	\ | 
 | 116 | 	} while (0) | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 117 | #define eprintk(a...) do { \ | 
 | 118 | 		printk(KERN_ERR a); \ | 
 | 119 | 		WARN_ON(1); \ | 
 | 120 | 	} while (0) | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 121 | #define MAX_CONFIG_LEN		40 | 
 | 122 |  | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 123 | static struct kgdb_io kgdbts_io_ops; | 
 | 124 | static char get_buf[BUFMAX]; | 
 | 125 | static int get_buf_cnt; | 
 | 126 | static char put_buf[BUFMAX]; | 
 | 127 | static int put_buf_cnt; | 
 | 128 | static char scratch_buf[BUFMAX]; | 
 | 129 | static int verbose; | 
 | 130 | static int repeat_test; | 
 | 131 | static int test_complete; | 
 | 132 | static int send_ack; | 
 | 133 | static int final_ack; | 
| Jason Wessel | b33cb81 | 2008-05-28 12:49:57 -0500 | [diff] [blame] | 134 | static int force_hwbrks; | 
 | 135 | static int hwbreaks_ok; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 136 | static int hw_break_val; | 
 | 137 | static int hw_break_val2; | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 138 | static int cont_instead_of_sstep; | 
 | 139 | static unsigned long cont_thread_id; | 
 | 140 | static unsigned long sstep_thread_id; | 
| David S. Miller | 4d7ffa4 | 2008-04-29 01:36:14 -0700 | [diff] [blame] | 141 | #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC) | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 142 | static int arch_needs_sstep_emulation = 1; | 
 | 143 | #else | 
 | 144 | static int arch_needs_sstep_emulation; | 
 | 145 | #endif | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 146 | static unsigned long cont_addr; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 147 | static unsigned long sstep_addr; | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 148 | static int restart_from_top_after_write; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 149 | static int sstep_state; | 
 | 150 |  | 
 | 151 | /* Storage for the registers, in GDB format. */ | 
 | 152 | static unsigned long kgdbts_gdb_regs[(NUMREGBYTES + | 
 | 153 | 					sizeof(unsigned long) - 1) / | 
 | 154 | 					sizeof(unsigned long)]; | 
 | 155 | static struct pt_regs kgdbts_regs; | 
 | 156 |  | 
 | 157 | /* -1 = init not run yet, 0 = unconfigured, 1 = configured. */ | 
 | 158 | static int configured		= -1; | 
 | 159 |  | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 160 | #ifdef CONFIG_KGDB_TESTS_BOOT_STRING | 
 | 161 | static char config[MAX_CONFIG_LEN] = CONFIG_KGDB_TESTS_BOOT_STRING; | 
 | 162 | #else | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 163 | static char config[MAX_CONFIG_LEN]; | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 164 | #endif | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 165 | static struct kparam_string kps = { | 
 | 166 | 	.string			= config, | 
 | 167 | 	.maxlen			= MAX_CONFIG_LEN, | 
 | 168 | }; | 
 | 169 |  | 
 | 170 | static void fill_get_buf(char *buf); | 
 | 171 |  | 
 | 172 | struct test_struct { | 
 | 173 | 	char *get; | 
 | 174 | 	char *put; | 
 | 175 | 	void (*get_handler)(char *); | 
 | 176 | 	int (*put_handler)(char *, char *); | 
 | 177 | }; | 
 | 178 |  | 
 | 179 | struct test_state { | 
 | 180 | 	char *name; | 
 | 181 | 	struct test_struct *tst; | 
 | 182 | 	int idx; | 
 | 183 | 	int (*run_test) (int, int); | 
 | 184 | 	int (*validate_put) (char *); | 
 | 185 | }; | 
 | 186 |  | 
 | 187 | static struct test_state ts; | 
 | 188 |  | 
 | 189 | static int kgdbts_unreg_thread(void *ptr) | 
 | 190 | { | 
 | 191 | 	/* Wait until the tests are complete and then ungresiter the I/O | 
 | 192 | 	 * driver. | 
 | 193 | 	 */ | 
 | 194 | 	while (!final_ack) | 
 | 195 | 		msleep_interruptible(1500); | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 196 | 	/* Pause for any other threads to exit after final ack. */ | 
 | 197 | 	msleep_interruptible(1000); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 198 | 	if (configured) | 
 | 199 | 		kgdb_unregister_io_module(&kgdbts_io_ops); | 
 | 200 | 	configured = 0; | 
 | 201 |  | 
 | 202 | 	return 0; | 
 | 203 | } | 
 | 204 |  | 
 | 205 | /* This is noinline such that it can be used for a single location to | 
 | 206 |  * place a breakpoint | 
 | 207 |  */ | 
 | 208 | static noinline void kgdbts_break_test(void) | 
 | 209 | { | 
 | 210 | 	v2printk("kgdbts: breakpoint complete\n"); | 
 | 211 | } | 
 | 212 |  | 
 | 213 | /* Lookup symbol info in the kernel */ | 
 | 214 | static unsigned long lookup_addr(char *arg) | 
 | 215 | { | 
 | 216 | 	unsigned long addr = 0; | 
 | 217 |  | 
 | 218 | 	if (!strcmp(arg, "kgdbts_break_test")) | 
 | 219 | 		addr = (unsigned long)kgdbts_break_test; | 
 | 220 | 	else if (!strcmp(arg, "sys_open")) | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 221 | 		addr = (unsigned long)do_sys_open; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 222 | 	else if (!strcmp(arg, "do_fork")) | 
 | 223 | 		addr = (unsigned long)do_fork; | 
 | 224 | 	else if (!strcmp(arg, "hw_break_val")) | 
 | 225 | 		addr = (unsigned long)&hw_break_val; | 
| Tiejun Chen | e78acf6 | 2013-02-27 11:09:27 +0800 | [diff] [blame] | 226 | 	addr = (unsigned long) dereference_function_descriptor((void *)addr); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 227 | 	return addr; | 
 | 228 | } | 
 | 229 |  | 
 | 230 | static void break_helper(char *bp_type, char *arg, unsigned long vaddr) | 
 | 231 | { | 
 | 232 | 	unsigned long addr; | 
 | 233 |  | 
 | 234 | 	if (arg) | 
 | 235 | 		addr = lookup_addr(arg); | 
 | 236 | 	else | 
 | 237 | 		addr = vaddr; | 
 | 238 |  | 
 | 239 | 	sprintf(scratch_buf, "%s,%lx,%i", bp_type, addr, | 
 | 240 | 		BREAK_INSTR_SIZE); | 
 | 241 | 	fill_get_buf(scratch_buf); | 
 | 242 | } | 
 | 243 |  | 
 | 244 | static void sw_break(char *arg) | 
 | 245 | { | 
| Jason Wessel | b33cb81 | 2008-05-28 12:49:57 -0500 | [diff] [blame] | 246 | 	break_helper(force_hwbrks ? "Z1" : "Z0", arg, 0); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 247 | } | 
 | 248 |  | 
 | 249 | static void sw_rem_break(char *arg) | 
 | 250 | { | 
| Jason Wessel | b33cb81 | 2008-05-28 12:49:57 -0500 | [diff] [blame] | 251 | 	break_helper(force_hwbrks ? "z1" : "z0", arg, 0); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 252 | } | 
 | 253 |  | 
 | 254 | static void hw_break(char *arg) | 
 | 255 | { | 
 | 256 | 	break_helper("Z1", arg, 0); | 
 | 257 | } | 
 | 258 |  | 
 | 259 | static void hw_rem_break(char *arg) | 
 | 260 | { | 
 | 261 | 	break_helper("z1", arg, 0); | 
 | 262 | } | 
 | 263 |  | 
 | 264 | static void hw_write_break(char *arg) | 
 | 265 | { | 
 | 266 | 	break_helper("Z2", arg, 0); | 
 | 267 | } | 
 | 268 |  | 
 | 269 | static void hw_rem_write_break(char *arg) | 
 | 270 | { | 
 | 271 | 	break_helper("z2", arg, 0); | 
 | 272 | } | 
 | 273 |  | 
 | 274 | static void hw_access_break(char *arg) | 
 | 275 | { | 
 | 276 | 	break_helper("Z4", arg, 0); | 
 | 277 | } | 
 | 278 |  | 
 | 279 | static void hw_rem_access_break(char *arg) | 
 | 280 | { | 
 | 281 | 	break_helper("z4", arg, 0); | 
 | 282 | } | 
 | 283 |  | 
 | 284 | static void hw_break_val_access(void) | 
 | 285 | { | 
 | 286 | 	hw_break_val2 = hw_break_val; | 
 | 287 | } | 
 | 288 |  | 
 | 289 | static void hw_break_val_write(void) | 
 | 290 | { | 
 | 291 | 	hw_break_val++; | 
 | 292 | } | 
 | 293 |  | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 294 | static int get_thread_id_continue(char *put_str, char *arg) | 
 | 295 | { | 
 | 296 | 	char *ptr = &put_str[11]; | 
 | 297 |  | 
 | 298 | 	if (put_str[1] != 'T' || put_str[2] != '0') | 
 | 299 | 		return 1; | 
 | 300 | 	kgdb_hex2long(&ptr, &cont_thread_id); | 
 | 301 | 	return 0; | 
 | 302 | } | 
 | 303 |  | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 304 | static int check_and_rewind_pc(char *put_str, char *arg) | 
 | 305 | { | 
 | 306 | 	unsigned long addr = lookup_addr(arg); | 
| Mike Frysinger | 63ab25e | 2011-05-26 16:25:45 -0700 | [diff] [blame] | 307 | 	unsigned long ip; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 308 | 	int offset = 0; | 
 | 309 |  | 
 | 310 | 	kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, | 
 | 311 | 		 NUMREGBYTES); | 
 | 312 | 	gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); | 
| Mike Frysinger | 63ab25e | 2011-05-26 16:25:45 -0700 | [diff] [blame] | 313 | 	ip = instruction_pointer(&kgdbts_regs); | 
 | 314 | 	v2printk("Stopped at IP: %lx\n", ip); | 
 | 315 | #ifdef GDB_ADJUSTS_BREAK_OFFSET | 
 | 316 | 	/* On some arches, a breakpoint stop requires it to be decremented */ | 
 | 317 | 	if (addr + BREAK_INSTR_SIZE == ip) | 
 | 318 | 		offset = -BREAK_INSTR_SIZE; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 319 | #endif | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 320 |  | 
 | 321 | 	if (arch_needs_sstep_emulation && sstep_addr && | 
 | 322 | 	    ip + offset == sstep_addr && | 
 | 323 | 	    ((!strcmp(arg, "sys_open") || !strcmp(arg, "do_fork")))) { | 
 | 324 | 		/* This is special case for emulated single step */ | 
 | 325 | 		v2printk("Emul: rewind hit single step bp\n"); | 
 | 326 | 		restart_from_top_after_write = 1; | 
 | 327 | 	} else if (strcmp(arg, "silent") && ip + offset != addr) { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 328 | 		eprintk("kgdbts: BP mismatch %lx expected %lx\n", | 
| Mike Frysinger | 63ab25e | 2011-05-26 16:25:45 -0700 | [diff] [blame] | 329 | 			   ip + offset, addr); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 330 | 		return 1; | 
 | 331 | 	} | 
| Mike Frysinger | 63ab25e | 2011-05-26 16:25:45 -0700 | [diff] [blame] | 332 | 	/* Readjust the instruction pointer if needed */ | 
| Mike Frysinger | 603d04b | 2011-05-28 10:04:25 -0400 | [diff] [blame] | 333 | 	ip += offset; | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 334 | 	cont_addr = ip; | 
| Mike Frysinger | 603d04b | 2011-05-28 10:04:25 -0400 | [diff] [blame] | 335 | #ifdef GDB_ADJUSTS_BREAK_OFFSET | 
 | 336 | 	instruction_pointer_set(&kgdbts_regs, ip); | 
 | 337 | #endif | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 338 | 	return 0; | 
 | 339 | } | 
 | 340 |  | 
 | 341 | static int check_single_step(char *put_str, char *arg) | 
 | 342 | { | 
 | 343 | 	unsigned long addr = lookup_addr(arg); | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 344 | 	static int matched_id; | 
 | 345 |  | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 346 | 	/* | 
 | 347 | 	 * From an arch indepent point of view the instruction pointer | 
 | 348 | 	 * should be on a different instruction | 
 | 349 | 	 */ | 
 | 350 | 	kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, | 
 | 351 | 		 NUMREGBYTES); | 
 | 352 | 	gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); | 
 | 353 | 	v2printk("Singlestep stopped at IP: %lx\n", | 
 | 354 | 		   instruction_pointer(&kgdbts_regs)); | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 355 |  | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 356 | 	if (sstep_thread_id != cont_thread_id) { | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 357 | 		/* | 
 | 358 | 		 * Ensure we stopped in the same thread id as before, else the | 
 | 359 | 		 * debugger should continue until the original thread that was | 
 | 360 | 		 * single stepped is scheduled again, emulating gdb's behavior. | 
 | 361 | 		 */ | 
 | 362 | 		v2printk("ThrID does not match: %lx\n", cont_thread_id); | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 363 | 		if (arch_needs_sstep_emulation) { | 
 | 364 | 			if (matched_id && | 
 | 365 | 			    instruction_pointer(&kgdbts_regs) != addr) | 
 | 366 | 				goto continue_test; | 
 | 367 | 			matched_id++; | 
 | 368 | 			ts.idx -= 2; | 
 | 369 | 			sstep_state = 0; | 
 | 370 | 			return 0; | 
 | 371 | 		} | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 372 | 		cont_instead_of_sstep = 1; | 
 | 373 | 		ts.idx -= 4; | 
 | 374 | 		return 0; | 
 | 375 | 	} | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 376 | continue_test: | 
 | 377 | 	matched_id = 0; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 378 | 	if (instruction_pointer(&kgdbts_regs) == addr) { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 379 | 		eprintk("kgdbts: SingleStep failed at %lx\n", | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 380 | 			   instruction_pointer(&kgdbts_regs)); | 
 | 381 | 		return 1; | 
 | 382 | 	} | 
 | 383 |  | 
 | 384 | 	return 0; | 
 | 385 | } | 
 | 386 |  | 
 | 387 | static void write_regs(char *arg) | 
 | 388 | { | 
 | 389 | 	memset(scratch_buf, 0, sizeof(scratch_buf)); | 
 | 390 | 	scratch_buf[0] = 'G'; | 
 | 391 | 	pt_regs_to_gdb_regs(kgdbts_gdb_regs, &kgdbts_regs); | 
 | 392 | 	kgdb_mem2hex((char *)kgdbts_gdb_regs, &scratch_buf[1], NUMREGBYTES); | 
 | 393 | 	fill_get_buf(scratch_buf); | 
 | 394 | } | 
 | 395 |  | 
 | 396 | static void skip_back_repeat_test(char *arg) | 
 | 397 | { | 
 | 398 | 	int go_back = simple_strtol(arg, NULL, 10); | 
 | 399 |  | 
 | 400 | 	repeat_test--; | 
 | 401 | 	if (repeat_test <= 0) | 
 | 402 | 		ts.idx++; | 
 | 403 | 	else | 
 | 404 | 		ts.idx -= go_back; | 
 | 405 | 	fill_get_buf(ts.tst[ts.idx].get); | 
 | 406 | } | 
 | 407 |  | 
 | 408 | static int got_break(char *put_str, char *arg) | 
 | 409 | { | 
 | 410 | 	test_complete = 1; | 
 | 411 | 	if (!strncmp(put_str+1, arg, 2)) { | 
 | 412 | 		if (!strncmp(arg, "T0", 2)) | 
 | 413 | 			test_complete = 2; | 
 | 414 | 		return 0; | 
 | 415 | 	} | 
 | 416 | 	return 1; | 
 | 417 | } | 
 | 418 |  | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 419 | static void get_cont_catch(char *arg) | 
 | 420 | { | 
 | 421 | 	/* Always send detach because the test is completed at this point */ | 
 | 422 | 	fill_get_buf("D"); | 
 | 423 | } | 
 | 424 |  | 
 | 425 | static int put_cont_catch(char *put_str, char *arg) | 
 | 426 | { | 
 | 427 | 	/* This is at the end of the test and we catch any and all input */ | 
 | 428 | 	v2printk("kgdbts: cleanup task: %lx\n", sstep_thread_id); | 
 | 429 | 	ts.idx--; | 
 | 430 | 	return 0; | 
 | 431 | } | 
 | 432 |  | 
 | 433 | static int emul_reset(char *put_str, char *arg) | 
 | 434 | { | 
 | 435 | 	if (strncmp(put_str, "$OK", 3)) | 
 | 436 | 		return 1; | 
 | 437 | 	if (restart_from_top_after_write) { | 
 | 438 | 		restart_from_top_after_write = 0; | 
 | 439 | 		ts.idx = -1; | 
 | 440 | 	} | 
 | 441 | 	return 0; | 
 | 442 | } | 
 | 443 |  | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 444 | static void emul_sstep_get(char *arg) | 
 | 445 | { | 
 | 446 | 	if (!arch_needs_sstep_emulation) { | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 447 | 		if (cont_instead_of_sstep) { | 
 | 448 | 			cont_instead_of_sstep = 0; | 
 | 449 | 			fill_get_buf("c"); | 
 | 450 | 		} else { | 
 | 451 | 			fill_get_buf(arg); | 
 | 452 | 		} | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 453 | 		return; | 
 | 454 | 	} | 
 | 455 | 	switch (sstep_state) { | 
 | 456 | 	case 0: | 
 | 457 | 		v2printk("Emulate single step\n"); | 
 | 458 | 		/* Start by looking at the current PC */ | 
 | 459 | 		fill_get_buf("g"); | 
 | 460 | 		break; | 
 | 461 | 	case 1: | 
 | 462 | 		/* set breakpoint */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 463 | 		break_helper("Z0", NULL, sstep_addr); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 464 | 		break; | 
 | 465 | 	case 2: | 
 | 466 | 		/* Continue */ | 
 | 467 | 		fill_get_buf("c"); | 
 | 468 | 		break; | 
 | 469 | 	case 3: | 
 | 470 | 		/* Clear breakpoint */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 471 | 		break_helper("z0", NULL, sstep_addr); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 472 | 		break; | 
 | 473 | 	default: | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 474 | 		eprintk("kgdbts: ERROR failed sstep get emulation\n"); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 475 | 	} | 
 | 476 | 	sstep_state++; | 
 | 477 | } | 
 | 478 |  | 
 | 479 | static int emul_sstep_put(char *put_str, char *arg) | 
 | 480 | { | 
 | 481 | 	if (!arch_needs_sstep_emulation) { | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 482 | 		char *ptr = &put_str[11]; | 
 | 483 | 		if (put_str[1] != 'T' || put_str[2] != '0') | 
 | 484 | 			return 1; | 
 | 485 | 		kgdb_hex2long(&ptr, &sstep_thread_id); | 
 | 486 | 		return 0; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 487 | 	} | 
 | 488 | 	switch (sstep_state) { | 
 | 489 | 	case 1: | 
 | 490 | 		/* validate the "g" packet to get the IP */ | 
 | 491 | 		kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, | 
 | 492 | 			 NUMREGBYTES); | 
 | 493 | 		gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); | 
 | 494 | 		v2printk("Stopped at IP: %lx\n", | 
 | 495 | 			 instruction_pointer(&kgdbts_regs)); | 
 | 496 | 		/* Want to stop at IP + break instruction size by default */ | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 497 | 		sstep_addr = cont_addr + BREAK_INSTR_SIZE; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 498 | 		break; | 
 | 499 | 	case 2: | 
 | 500 | 		if (strncmp(put_str, "$OK", 3)) { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 501 | 			eprintk("kgdbts: failed sstep break set\n"); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 502 | 			return 1; | 
 | 503 | 		} | 
 | 504 | 		break; | 
 | 505 | 	case 3: | 
 | 506 | 		if (strncmp(put_str, "$T0", 3)) { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 507 | 			eprintk("kgdbts: failed continue sstep\n"); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 508 | 			return 1; | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 509 | 		} else { | 
 | 510 | 			char *ptr = &put_str[11]; | 
 | 511 | 			kgdb_hex2long(&ptr, &sstep_thread_id); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 512 | 		} | 
 | 513 | 		break; | 
 | 514 | 	case 4: | 
 | 515 | 		if (strncmp(put_str, "$OK", 3)) { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 516 | 			eprintk("kgdbts: failed sstep break unset\n"); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 517 | 			return 1; | 
 | 518 | 		} | 
 | 519 | 		/* Single step is complete so continue on! */ | 
 | 520 | 		sstep_state = 0; | 
 | 521 | 		return 0; | 
 | 522 | 	default: | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 523 | 		eprintk("kgdbts: ERROR failed sstep put emulation\n"); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 524 | 	} | 
 | 525 |  | 
 | 526 | 	/* Continue on the same test line until emulation is complete */ | 
 | 527 | 	ts.idx--; | 
 | 528 | 	return 0; | 
 | 529 | } | 
 | 530 |  | 
 | 531 | static int final_ack_set(char *put_str, char *arg) | 
 | 532 | { | 
 | 533 | 	if (strncmp(put_str+1, arg, 2)) | 
 | 534 | 		return 1; | 
 | 535 | 	final_ack = 1; | 
 | 536 | 	return 0; | 
 | 537 | } | 
 | 538 | /* | 
 | 539 |  * Test to plant a breakpoint and detach, which should clear out the | 
 | 540 |  * breakpoint and restore the original instruction. | 
 | 541 |  */ | 
 | 542 | static struct test_struct plant_and_detach_test[] = { | 
 | 543 | 	{ "?", "S0*" }, /* Clear break points */ | 
 | 544 | 	{ "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ | 
 | 545 | 	{ "D", "OK" }, /* Detach */ | 
 | 546 | 	{ "", "" }, | 
 | 547 | }; | 
 | 548 |  | 
 | 549 | /* | 
 | 550 |  * Simple test to write in a software breakpoint, check for the | 
 | 551 |  * correct stop location and detach. | 
 | 552 |  */ | 
 | 553 | static struct test_struct sw_breakpoint_test[] = { | 
 | 554 | 	{ "?", "S0*" }, /* Clear break points */ | 
 | 555 | 	{ "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ | 
 | 556 | 	{ "c", "T0*", }, /* Continue */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 557 | 	{ "g", "kgdbts_break_test", NULL, check_and_rewind_pc }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 558 | 	{ "write", "OK", write_regs }, | 
 | 559 | 	{ "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */ | 
 | 560 | 	{ "D", "OK" }, /* Detach */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 561 | 	{ "D", "OK", NULL,  got_break }, /* On success we made it here */ | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 562 | 	{ "", "" }, | 
 | 563 | }; | 
 | 564 |  | 
 | 565 | /* | 
 | 566 |  * Test a known bad memory read location to test the fault handler and | 
 | 567 |  * read bytes 1-8 at the bad address | 
 | 568 |  */ | 
 | 569 | static struct test_struct bad_read_test[] = { | 
 | 570 | 	{ "?", "S0*" }, /* Clear break points */ | 
 | 571 | 	{ "m0,1", "E*" }, /* read 1 byte at address 1 */ | 
 | 572 | 	{ "m0,2", "E*" }, /* read 1 byte at address 2 */ | 
 | 573 | 	{ "m0,3", "E*" }, /* read 1 byte at address 3 */ | 
 | 574 | 	{ "m0,4", "E*" }, /* read 1 byte at address 4 */ | 
 | 575 | 	{ "m0,5", "E*" }, /* read 1 byte at address 5 */ | 
 | 576 | 	{ "m0,6", "E*" }, /* read 1 byte at address 6 */ | 
 | 577 | 	{ "m0,7", "E*" }, /* read 1 byte at address 7 */ | 
 | 578 | 	{ "m0,8", "E*" }, /* read 1 byte at address 8 */ | 
 | 579 | 	{ "D", "OK" }, /* Detach which removes all breakpoints and continues */ | 
 | 580 | 	{ "", "" }, | 
 | 581 | }; | 
 | 582 |  | 
 | 583 | /* | 
 | 584 |  * Test for hitting a breakpoint, remove it, single step, plant it | 
 | 585 |  * again and detach. | 
 | 586 |  */ | 
 | 587 | static struct test_struct singlestep_break_test[] = { | 
 | 588 | 	{ "?", "S0*" }, /* Clear break points */ | 
 | 589 | 	{ "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 590 | 	{ "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ | 
 | 591 | 	{ "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 592 | 	{ "g", "kgdbts_break_test", NULL, check_and_rewind_pc }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 593 | 	{ "write", "OK", write_regs }, /* Write registers */ | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 594 | 	{ "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 595 | 	{ "g", "kgdbts_break_test", NULL, check_single_step }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 596 | 	{ "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ | 
 | 597 | 	{ "c", "T0*", }, /* Continue */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 598 | 	{ "g", "kgdbts_break_test", NULL, check_and_rewind_pc }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 599 | 	{ "write", "OK", write_regs }, /* Write registers */ | 
 | 600 | 	{ "D", "OK" }, /* Remove all breakpoints and continues */ | 
 | 601 | 	{ "", "" }, | 
 | 602 | }; | 
 | 603 |  | 
 | 604 | /* | 
 | 605 |  * Test for hitting a breakpoint at do_fork for what ever the number | 
 | 606 |  * of iterations required by the variable repeat_test. | 
 | 607 |  */ | 
 | 608 | static struct test_struct do_fork_test[] = { | 
 | 609 | 	{ "?", "S0*" }, /* Clear break points */ | 
 | 610 | 	{ "do_fork", "OK", sw_break, }, /* set sw breakpoint */ | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 611 | 	{ "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ | 
 | 612 | 	{ "do_fork", "OK", sw_rem_break }, /*remove breakpoint */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 613 | 	{ "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */ | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 614 | 	{ "write", "OK", write_regs, emul_reset }, /* Write registers */ | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 615 | 	{ "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 616 | 	{ "g", "do_fork", NULL, check_single_step }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 617 | 	{ "do_fork", "OK", sw_break, }, /* set sw breakpoint */ | 
 | 618 | 	{ "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 619 | 	{ "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 620 | 	{ "", "", get_cont_catch, put_cont_catch }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 621 | }; | 
 | 622 |  | 
 | 623 | /* Test for hitting a breakpoint at sys_open for what ever the number | 
 | 624 |  * of iterations required by the variable repeat_test. | 
 | 625 |  */ | 
 | 626 | static struct test_struct sys_open_test[] = { | 
 | 627 | 	{ "?", "S0*" }, /* Clear break points */ | 
 | 628 | 	{ "sys_open", "OK", sw_break, }, /* set sw breakpoint */ | 
| Jason Wessel | 486c598 | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 629 | 	{ "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ | 
 | 630 | 	{ "sys_open", "OK", sw_rem_break }, /*remove breakpoint */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 631 | 	{ "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */ | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 632 | 	{ "write", "OK", write_regs, emul_reset }, /* Write registers */ | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 633 | 	{ "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 634 | 	{ "g", "sys_open", NULL, check_single_step }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 635 | 	{ "sys_open", "OK", sw_break, }, /* set sw breakpoint */ | 
 | 636 | 	{ "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 637 | 	{ "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 638 | 	{ "", "", get_cont_catch, put_cont_catch }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 639 | }; | 
 | 640 |  | 
 | 641 | /* | 
 | 642 |  * Test for hitting a simple hw breakpoint | 
 | 643 |  */ | 
 | 644 | static struct test_struct hw_breakpoint_test[] = { | 
 | 645 | 	{ "?", "S0*" }, /* Clear break points */ | 
 | 646 | 	{ "kgdbts_break_test", "OK", hw_break, }, /* set hw breakpoint */ | 
 | 647 | 	{ "c", "T0*", }, /* Continue */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 648 | 	{ "g", "kgdbts_break_test", NULL, check_and_rewind_pc }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 649 | 	{ "write", "OK", write_regs }, | 
 | 650 | 	{ "kgdbts_break_test", "OK", hw_rem_break }, /*remove breakpoint */ | 
 | 651 | 	{ "D", "OK" }, /* Detach */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 652 | 	{ "D", "OK", NULL,  got_break }, /* On success we made it here */ | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 653 | 	{ "", "" }, | 
 | 654 | }; | 
 | 655 |  | 
 | 656 | /* | 
 | 657 |  * Test for hitting a hw write breakpoint | 
 | 658 |  */ | 
 | 659 | static struct test_struct hw_write_break_test[] = { | 
 | 660 | 	{ "?", "S0*" }, /* Clear break points */ | 
 | 661 | 	{ "hw_break_val", "OK", hw_write_break, }, /* set hw breakpoint */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 662 | 	{ "c", "T0*", NULL, got_break }, /* Continue */ | 
 | 663 | 	{ "g", "silent", NULL, check_and_rewind_pc }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 664 | 	{ "write", "OK", write_regs }, | 
 | 665 | 	{ "hw_break_val", "OK", hw_rem_write_break }, /*remove breakpoint */ | 
 | 666 | 	{ "D", "OK" }, /* Detach */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 667 | 	{ "D", "OK", NULL,  got_break }, /* On success we made it here */ | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 668 | 	{ "", "" }, | 
 | 669 | }; | 
 | 670 |  | 
 | 671 | /* | 
 | 672 |  * Test for hitting a hw access breakpoint | 
 | 673 |  */ | 
 | 674 | static struct test_struct hw_access_break_test[] = { | 
 | 675 | 	{ "?", "S0*" }, /* Clear break points */ | 
 | 676 | 	{ "hw_break_val", "OK", hw_access_break, }, /* set hw breakpoint */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 677 | 	{ "c", "T0*", NULL, got_break }, /* Continue */ | 
 | 678 | 	{ "g", "silent", NULL, check_and_rewind_pc }, | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 679 | 	{ "write", "OK", write_regs }, | 
 | 680 | 	{ "hw_break_val", "OK", hw_rem_access_break }, /*remove breakpoint */ | 
 | 681 | 	{ "D", "OK" }, /* Detach */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 682 | 	{ "D", "OK", NULL,  got_break }, /* On success we made it here */ | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 683 | 	{ "", "" }, | 
 | 684 | }; | 
 | 685 |  | 
 | 686 | /* | 
 | 687 |  * Test for hitting a hw access breakpoint | 
 | 688 |  */ | 
 | 689 | static struct test_struct nmi_sleep_test[] = { | 
 | 690 | 	{ "?", "S0*" }, /* Clear break points */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 691 | 	{ "c", "T0*", NULL, got_break }, /* Continue */ | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 692 | 	{ "D", "OK" }, /* Detach */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 693 | 	{ "D", "OK", NULL,  got_break }, /* On success we made it here */ | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 694 | 	{ "", "" }, | 
 | 695 | }; | 
 | 696 |  | 
 | 697 | static void fill_get_buf(char *buf) | 
 | 698 | { | 
 | 699 | 	unsigned char checksum = 0; | 
 | 700 | 	int count = 0; | 
 | 701 | 	char ch; | 
 | 702 |  | 
 | 703 | 	strcpy(get_buf, "$"); | 
 | 704 | 	strcat(get_buf, buf); | 
 | 705 | 	while ((ch = buf[count])) { | 
 | 706 | 		checksum += ch; | 
 | 707 | 		count++; | 
 | 708 | 	} | 
 | 709 | 	strcat(get_buf, "#"); | 
| Harvey Harrison | 827e609 | 2008-05-28 12:49:56 -0500 | [diff] [blame] | 710 | 	get_buf[count + 2] = hex_asc_hi(checksum); | 
 | 711 | 	get_buf[count + 3] = hex_asc_lo(checksum); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 712 | 	get_buf[count + 4] = '\0'; | 
 | 713 | 	v2printk("get%i: %s\n", ts.idx, get_buf); | 
 | 714 | } | 
 | 715 |  | 
 | 716 | static int validate_simple_test(char *put_str) | 
 | 717 | { | 
 | 718 | 	char *chk_str; | 
 | 719 |  | 
 | 720 | 	if (ts.tst[ts.idx].put_handler) | 
 | 721 | 		return ts.tst[ts.idx].put_handler(put_str, | 
 | 722 | 			ts.tst[ts.idx].put); | 
 | 723 |  | 
 | 724 | 	chk_str = ts.tst[ts.idx].put; | 
 | 725 | 	if (*put_str == '$') | 
 | 726 | 		put_str++; | 
 | 727 |  | 
 | 728 | 	while (*chk_str != '\0' && *put_str != '\0') { | 
 | 729 | 		/* If someone does a * to match the rest of the string, allow | 
| Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 730 | 		 * it, or stop if the received string is complete. | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 731 | 		 */ | 
 | 732 | 		if (*put_str == '#' || *chk_str == '*') | 
 | 733 | 			return 0; | 
 | 734 | 		if (*put_str != *chk_str) | 
 | 735 | 			return 1; | 
 | 736 |  | 
 | 737 | 		chk_str++; | 
 | 738 | 		put_str++; | 
 | 739 | 	} | 
 | 740 | 	if (*chk_str == '\0' && (*put_str == '\0' || *put_str == '#')) | 
 | 741 | 		return 0; | 
 | 742 |  | 
 | 743 | 	return 1; | 
 | 744 | } | 
 | 745 |  | 
 | 746 | static int run_simple_test(int is_get_char, int chr) | 
 | 747 | { | 
 | 748 | 	int ret = 0; | 
 | 749 | 	if (is_get_char) { | 
 | 750 | 		/* Send an ACK on the get if a prior put completed and set the | 
 | 751 | 		 * send ack variable | 
 | 752 | 		 */ | 
 | 753 | 		if (send_ack) { | 
 | 754 | 			send_ack = 0; | 
 | 755 | 			return '+'; | 
 | 756 | 		} | 
 | 757 | 		/* On the first get char, fill the transmit buffer and then | 
 | 758 | 		 * take from the get_string. | 
 | 759 | 		 */ | 
 | 760 | 		if (get_buf_cnt == 0) { | 
 | 761 | 			if (ts.tst[ts.idx].get_handler) | 
 | 762 | 				ts.tst[ts.idx].get_handler(ts.tst[ts.idx].get); | 
 | 763 | 			else | 
 | 764 | 				fill_get_buf(ts.tst[ts.idx].get); | 
 | 765 | 		} | 
 | 766 |  | 
 | 767 | 		if (get_buf[get_buf_cnt] == '\0') { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 768 | 			eprintk("kgdbts: ERROR GET: EOB on '%s' at %i\n", | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 769 | 			   ts.name, ts.idx); | 
 | 770 | 			get_buf_cnt = 0; | 
 | 771 | 			fill_get_buf("D"); | 
 | 772 | 		} | 
 | 773 | 		ret = get_buf[get_buf_cnt]; | 
 | 774 | 		get_buf_cnt++; | 
 | 775 | 		return ret; | 
 | 776 | 	} | 
 | 777 |  | 
 | 778 | 	/* This callback is a put char which is when kgdb sends data to | 
 | 779 | 	 * this I/O module. | 
 | 780 | 	 */ | 
| Jason Wessel | 23bbd8e | 2012-03-29 17:41:24 -0500 | [diff] [blame] | 781 | 	if (ts.tst[ts.idx].get[0] == '\0' && ts.tst[ts.idx].put[0] == '\0' && | 
 | 782 | 	    !ts.tst[ts.idx].get_handler) { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 783 | 		eprintk("kgdbts: ERROR: beyond end of test on" | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 784 | 			   " '%s' line %i\n", ts.name, ts.idx); | 
 | 785 | 		return 0; | 
 | 786 | 	} | 
 | 787 |  | 
 | 788 | 	if (put_buf_cnt >= BUFMAX) { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 789 | 		eprintk("kgdbts: ERROR: put buffer overflow on" | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 790 | 			   " '%s' line %i\n", ts.name, ts.idx); | 
 | 791 | 		put_buf_cnt = 0; | 
 | 792 | 		return 0; | 
 | 793 | 	} | 
 | 794 | 	/* Ignore everything until the first valid packet start '$' */ | 
 | 795 | 	if (put_buf_cnt == 0 && chr != '$') | 
 | 796 | 		return 0; | 
 | 797 |  | 
 | 798 | 	put_buf[put_buf_cnt] = chr; | 
 | 799 | 	put_buf_cnt++; | 
 | 800 |  | 
 | 801 | 	/* End of packet == #XX so look for the '#' */ | 
 | 802 | 	if (put_buf_cnt > 3 && put_buf[put_buf_cnt - 3] == '#') { | 
| Roel Kluin | b4f1b67 | 2009-12-11 08:43:15 -0600 | [diff] [blame] | 803 | 		if (put_buf_cnt >= BUFMAX) { | 
 | 804 | 			eprintk("kgdbts: ERROR: put buffer overflow on" | 
 | 805 | 				" '%s' line %i\n", ts.name, ts.idx); | 
 | 806 | 			put_buf_cnt = 0; | 
 | 807 | 			return 0; | 
 | 808 | 		} | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 809 | 		put_buf[put_buf_cnt] = '\0'; | 
 | 810 | 		v2printk("put%i: %s\n", ts.idx, put_buf); | 
 | 811 | 		/* Trigger check here */ | 
 | 812 | 		if (ts.validate_put && ts.validate_put(put_buf)) { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 813 | 			eprintk("kgdbts: ERROR PUT: end of test " | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 814 | 			   "buffer on '%s' line %i expected %s got %s\n", | 
 | 815 | 			   ts.name, ts.idx, ts.tst[ts.idx].put, put_buf); | 
 | 816 | 		} | 
 | 817 | 		ts.idx++; | 
 | 818 | 		put_buf_cnt = 0; | 
 | 819 | 		get_buf_cnt = 0; | 
 | 820 | 		send_ack = 1; | 
 | 821 | 	} | 
 | 822 | 	return 0; | 
 | 823 | } | 
 | 824 |  | 
 | 825 | static void init_simple_test(void) | 
 | 826 | { | 
 | 827 | 	memset(&ts, 0, sizeof(ts)); | 
 | 828 | 	ts.run_test = run_simple_test; | 
 | 829 | 	ts.validate_put = validate_simple_test; | 
 | 830 | } | 
 | 831 |  | 
 | 832 | static void run_plant_and_detach_test(int is_early) | 
 | 833 | { | 
 | 834 | 	char before[BREAK_INSTR_SIZE]; | 
 | 835 | 	char after[BREAK_INSTR_SIZE]; | 
 | 836 |  | 
 | 837 | 	probe_kernel_read(before, (char *)kgdbts_break_test, | 
 | 838 | 	  BREAK_INSTR_SIZE); | 
 | 839 | 	init_simple_test(); | 
 | 840 | 	ts.tst = plant_and_detach_test; | 
 | 841 | 	ts.name = "plant_and_detach_test"; | 
 | 842 | 	/* Activate test with initial breakpoint */ | 
 | 843 | 	if (!is_early) | 
 | 844 | 		kgdb_breakpoint(); | 
 | 845 | 	probe_kernel_read(after, (char *)kgdbts_break_test, | 
 | 846 | 	  BREAK_INSTR_SIZE); | 
 | 847 | 	if (memcmp(before, after, BREAK_INSTR_SIZE)) { | 
 | 848 | 		printk(KERN_CRIT "kgdbts: ERROR kgdb corrupted memory\n"); | 
 | 849 | 		panic("kgdb memory corruption"); | 
 | 850 | 	} | 
 | 851 |  | 
 | 852 | 	/* complete the detach test */ | 
 | 853 | 	if (!is_early) | 
 | 854 | 		kgdbts_break_test(); | 
 | 855 | } | 
 | 856 |  | 
 | 857 | static void run_breakpoint_test(int is_hw_breakpoint) | 
 | 858 | { | 
 | 859 | 	test_complete = 0; | 
 | 860 | 	init_simple_test(); | 
 | 861 | 	if (is_hw_breakpoint) { | 
 | 862 | 		ts.tst = hw_breakpoint_test; | 
 | 863 | 		ts.name = "hw_breakpoint_test"; | 
 | 864 | 	} else { | 
 | 865 | 		ts.tst = sw_breakpoint_test; | 
 | 866 | 		ts.name = "sw_breakpoint_test"; | 
 | 867 | 	} | 
 | 868 | 	/* Activate test with initial breakpoint */ | 
 | 869 | 	kgdb_breakpoint(); | 
 | 870 | 	/* run code with the break point in it */ | 
 | 871 | 	kgdbts_break_test(); | 
 | 872 | 	kgdb_breakpoint(); | 
 | 873 |  | 
 | 874 | 	if (test_complete) | 
 | 875 | 		return; | 
 | 876 |  | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 877 | 	eprintk("kgdbts: ERROR %s test failed\n", ts.name); | 
| Jason Wessel | b33cb81 | 2008-05-28 12:49:57 -0500 | [diff] [blame] | 878 | 	if (is_hw_breakpoint) | 
 | 879 | 		hwbreaks_ok = 0; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 880 | } | 
 | 881 |  | 
 | 882 | static void run_hw_break_test(int is_write_test) | 
 | 883 | { | 
 | 884 | 	test_complete = 0; | 
 | 885 | 	init_simple_test(); | 
 | 886 | 	if (is_write_test) { | 
 | 887 | 		ts.tst = hw_write_break_test; | 
 | 888 | 		ts.name = "hw_write_break_test"; | 
 | 889 | 	} else { | 
 | 890 | 		ts.tst = hw_access_break_test; | 
 | 891 | 		ts.name = "hw_access_break_test"; | 
 | 892 | 	} | 
 | 893 | 	/* Activate test with initial breakpoint */ | 
 | 894 | 	kgdb_breakpoint(); | 
 | 895 | 	hw_break_val_access(); | 
 | 896 | 	if (is_write_test) { | 
| Jason Wessel | b33cb81 | 2008-05-28 12:49:57 -0500 | [diff] [blame] | 897 | 		if (test_complete == 2) { | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 898 | 			eprintk("kgdbts: ERROR %s broke on access\n", | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 899 | 				ts.name); | 
| Jason Wessel | b33cb81 | 2008-05-28 12:49:57 -0500 | [diff] [blame] | 900 | 			hwbreaks_ok = 0; | 
 | 901 | 		} | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 902 | 		hw_break_val_write(); | 
 | 903 | 	} | 
 | 904 | 	kgdb_breakpoint(); | 
 | 905 |  | 
 | 906 | 	if (test_complete == 1) | 
 | 907 | 		return; | 
 | 908 |  | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 909 | 	eprintk("kgdbts: ERROR %s test failed\n", ts.name); | 
| Jason Wessel | b33cb81 | 2008-05-28 12:49:57 -0500 | [diff] [blame] | 910 | 	hwbreaks_ok = 0; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 911 | } | 
 | 912 |  | 
 | 913 | static void run_nmi_sleep_test(int nmi_sleep) | 
 | 914 | { | 
 | 915 | 	unsigned long flags; | 
 | 916 |  | 
 | 917 | 	init_simple_test(); | 
 | 918 | 	ts.tst = nmi_sleep_test; | 
 | 919 | 	ts.name = "nmi_sleep_test"; | 
 | 920 | 	/* Activate test with initial breakpoint */ | 
 | 921 | 	kgdb_breakpoint(); | 
 | 922 | 	local_irq_save(flags); | 
 | 923 | 	mdelay(nmi_sleep*1000); | 
 | 924 | 	touch_nmi_watchdog(); | 
 | 925 | 	local_irq_restore(flags); | 
 | 926 | 	if (test_complete != 2) | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 927 | 		eprintk("kgdbts: ERROR nmi_test did not hit nmi\n"); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 928 | 	kgdb_breakpoint(); | 
 | 929 | 	if (test_complete == 1) | 
 | 930 | 		return; | 
 | 931 |  | 
| Jason Wessel | 974460c | 2008-03-20 13:43:44 -0500 | [diff] [blame] | 932 | 	eprintk("kgdbts: ERROR %s test failed\n", ts.name); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 933 | } | 
 | 934 |  | 
 | 935 | static void run_bad_read_test(void) | 
 | 936 | { | 
 | 937 | 	init_simple_test(); | 
 | 938 | 	ts.tst = bad_read_test; | 
 | 939 | 	ts.name = "bad_read_test"; | 
 | 940 | 	/* Activate test with initial breakpoint */ | 
 | 941 | 	kgdb_breakpoint(); | 
 | 942 | } | 
 | 943 |  | 
 | 944 | static void run_do_fork_test(void) | 
 | 945 | { | 
 | 946 | 	init_simple_test(); | 
 | 947 | 	ts.tst = do_fork_test; | 
 | 948 | 	ts.name = "do_fork_test"; | 
 | 949 | 	/* Activate test with initial breakpoint */ | 
 | 950 | 	kgdb_breakpoint(); | 
 | 951 | } | 
 | 952 |  | 
 | 953 | static void run_sys_open_test(void) | 
 | 954 | { | 
 | 955 | 	init_simple_test(); | 
 | 956 | 	ts.tst = sys_open_test; | 
 | 957 | 	ts.name = "sys_open_test"; | 
 | 958 | 	/* Activate test with initial breakpoint */ | 
 | 959 | 	kgdb_breakpoint(); | 
 | 960 | } | 
 | 961 |  | 
 | 962 | static void run_singlestep_break_test(void) | 
 | 963 | { | 
 | 964 | 	init_simple_test(); | 
 | 965 | 	ts.tst = singlestep_break_test; | 
 | 966 | 	ts.name = "singlestep_breakpoint_test"; | 
 | 967 | 	/* Activate test with initial breakpoint */ | 
 | 968 | 	kgdb_breakpoint(); | 
 | 969 | 	kgdbts_break_test(); | 
 | 970 | 	kgdbts_break_test(); | 
 | 971 | } | 
 | 972 |  | 
 | 973 | static void kgdbts_run_tests(void) | 
 | 974 | { | 
 | 975 | 	char *ptr; | 
 | 976 | 	int fork_test = 0; | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 977 | 	int do_sys_open_test = 0; | 
| Jason Wessel | 7cfcd98 | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 978 | 	int sstep_test = 1000; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 979 | 	int nmi_sleep = 0; | 
| Jason Wessel | 7cfcd98 | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 980 | 	int i; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 981 |  | 
| Geert Uytterhoeven | 59d309f | 2009-12-11 08:43:15 -0600 | [diff] [blame] | 982 | 	ptr = strchr(config, 'F'); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 983 | 	if (ptr) | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 984 | 		fork_test = simple_strtol(ptr + 1, NULL, 10); | 
| Geert Uytterhoeven | 59d309f | 2009-12-11 08:43:15 -0600 | [diff] [blame] | 985 | 	ptr = strchr(config, 'S'); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 986 | 	if (ptr) | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 987 | 		do_sys_open_test = simple_strtol(ptr + 1, NULL, 10); | 
| Geert Uytterhoeven | 59d309f | 2009-12-11 08:43:15 -0600 | [diff] [blame] | 988 | 	ptr = strchr(config, 'N'); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 989 | 	if (ptr) | 
 | 990 | 		nmi_sleep = simple_strtol(ptr+1, NULL, 10); | 
| Geert Uytterhoeven | 59d309f | 2009-12-11 08:43:15 -0600 | [diff] [blame] | 991 | 	ptr = strchr(config, 'I'); | 
| Jason Wessel | 7cfcd98 | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 992 | 	if (ptr) | 
 | 993 | 		sstep_test = simple_strtol(ptr+1, NULL, 10); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 994 |  | 
| Jason Wessel | 456ca7f | 2012-03-29 06:55:44 -0500 | [diff] [blame] | 995 | 	/* All HW break point tests */ | 
 | 996 | 	if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) { | 
 | 997 | 		hwbreaks_ok = 1; | 
 | 998 | 		v1printk("kgdbts:RUN hw breakpoint test\n"); | 
 | 999 | 		run_breakpoint_test(1); | 
 | 1000 | 		v1printk("kgdbts:RUN hw write breakpoint test\n"); | 
 | 1001 | 		run_hw_break_test(1); | 
 | 1002 | 		v1printk("kgdbts:RUN access write breakpoint test\n"); | 
 | 1003 | 		run_hw_break_test(0); | 
 | 1004 | 	} | 
| Jason Wessel | 456ca7f | 2012-03-29 06:55:44 -0500 | [diff] [blame] | 1005 |  | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1006 | 	/* required internal KGDB tests */ | 
 | 1007 | 	v1printk("kgdbts:RUN plant and detach test\n"); | 
 | 1008 | 	run_plant_and_detach_test(0); | 
 | 1009 | 	v1printk("kgdbts:RUN sw breakpoint test\n"); | 
 | 1010 | 	run_breakpoint_test(0); | 
 | 1011 | 	v1printk("kgdbts:RUN bad memory access test\n"); | 
 | 1012 | 	run_bad_read_test(); | 
| Jason Wessel | 7cfcd98 | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 1013 | 	v1printk("kgdbts:RUN singlestep test %i iterations\n", sstep_test); | 
 | 1014 | 	for (i = 0; i < sstep_test; i++) { | 
 | 1015 | 		run_singlestep_break_test(); | 
 | 1016 | 		if (i % 100 == 0) | 
 | 1017 | 			v1printk("kgdbts:RUN singlestep [%i/%i]\n", | 
 | 1018 | 				 i, sstep_test); | 
 | 1019 | 	} | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1020 |  | 
 | 1021 | 	/* ===Optional tests=== */ | 
 | 1022 |  | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1023 | 	if (nmi_sleep) { | 
 | 1024 | 		v1printk("kgdbts:RUN NMI sleep %i seconds test\n", nmi_sleep); | 
 | 1025 | 		run_nmi_sleep_test(nmi_sleep); | 
 | 1026 | 	} | 
 | 1027 |  | 
 | 1028 | 	/* If the do_fork test is run it will be the last test that is | 
 | 1029 | 	 * executed because a kernel thread will be spawned at the very | 
 | 1030 | 	 * end to unregister the debug hooks. | 
 | 1031 | 	 */ | 
 | 1032 | 	if (fork_test) { | 
 | 1033 | 		repeat_test = fork_test; | 
 | 1034 | 		printk(KERN_INFO "kgdbts:RUN do_fork for %i breakpoints\n", | 
 | 1035 | 			repeat_test); | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 1036 | 		kthread_run(kgdbts_unreg_thread, NULL, "kgdbts_unreg"); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1037 | 		run_do_fork_test(); | 
 | 1038 | 		return; | 
 | 1039 | 	} | 
 | 1040 |  | 
 | 1041 | 	/* If the sys_open test is run it will be the last test that is | 
 | 1042 | 	 * executed because a kernel thread will be spawned at the very | 
 | 1043 | 	 * end to unregister the debug hooks. | 
 | 1044 | 	 */ | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 1045 | 	if (do_sys_open_test) { | 
 | 1046 | 		repeat_test = do_sys_open_test; | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1047 | 		printk(KERN_INFO "kgdbts:RUN sys_open for %i breakpoints\n", | 
 | 1048 | 			repeat_test); | 
| Harvey Harrison | 001fddf | 2008-04-24 16:57:23 -0500 | [diff] [blame] | 1049 | 		kthread_run(kgdbts_unreg_thread, NULL, "kgdbts_unreg"); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1050 | 		run_sys_open_test(); | 
 | 1051 | 		return; | 
 | 1052 | 	} | 
 | 1053 | 	/* Shutdown and unregister */ | 
 | 1054 | 	kgdb_unregister_io_module(&kgdbts_io_ops); | 
 | 1055 | 	configured = 0; | 
 | 1056 | } | 
 | 1057 |  | 
 | 1058 | static int kgdbts_option_setup(char *opt) | 
 | 1059 | { | 
| Dan Carpenter | adb4b83 | 2010-03-15 07:28:00 -0500 | [diff] [blame] | 1060 | 	if (strlen(opt) >= MAX_CONFIG_LEN) { | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1061 | 		printk(KERN_ERR "kgdbts: config string too long\n"); | 
 | 1062 | 		return -ENOSPC; | 
 | 1063 | 	} | 
 | 1064 | 	strcpy(config, opt); | 
 | 1065 |  | 
 | 1066 | 	verbose = 0; | 
 | 1067 | 	if (strstr(config, "V1")) | 
 | 1068 | 		verbose = 1; | 
 | 1069 | 	if (strstr(config, "V2")) | 
 | 1070 | 		verbose = 2; | 
 | 1071 |  | 
 | 1072 | 	return 0; | 
 | 1073 | } | 
 | 1074 |  | 
 | 1075 | __setup("kgdbts=", kgdbts_option_setup); | 
 | 1076 |  | 
 | 1077 | static int configure_kgdbts(void) | 
 | 1078 | { | 
 | 1079 | 	int err = 0; | 
 | 1080 |  | 
 | 1081 | 	if (!strlen(config) || isspace(config[0])) | 
 | 1082 | 		goto noconfig; | 
 | 1083 | 	err = kgdbts_option_setup(config); | 
 | 1084 | 	if (err) | 
 | 1085 | 		goto noconfig; | 
 | 1086 |  | 
 | 1087 | 	final_ack = 0; | 
 | 1088 | 	run_plant_and_detach_test(1); | 
 | 1089 |  | 
 | 1090 | 	err = kgdb_register_io_module(&kgdbts_io_ops); | 
 | 1091 | 	if (err) { | 
 | 1092 | 		configured = 0; | 
 | 1093 | 		return err; | 
 | 1094 | 	} | 
 | 1095 | 	configured = 1; | 
 | 1096 | 	kgdbts_run_tests(); | 
 | 1097 |  | 
 | 1098 | 	return err; | 
 | 1099 |  | 
 | 1100 | noconfig: | 
 | 1101 | 	config[0] = 0; | 
 | 1102 | 	configured = 0; | 
 | 1103 |  | 
 | 1104 | 	return err; | 
 | 1105 | } | 
 | 1106 |  | 
 | 1107 | static int __init init_kgdbts(void) | 
 | 1108 | { | 
 | 1109 | 	/* Already configured? */ | 
 | 1110 | 	if (configured == 1) | 
 | 1111 | 		return 0; | 
 | 1112 |  | 
 | 1113 | 	return configure_kgdbts(); | 
 | 1114 | } | 
 | 1115 |  | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1116 | static int kgdbts_get_char(void) | 
 | 1117 | { | 
 | 1118 | 	int val = 0; | 
 | 1119 |  | 
 | 1120 | 	if (ts.run_test) | 
 | 1121 | 		val = ts.run_test(1, 0); | 
 | 1122 |  | 
 | 1123 | 	return val; | 
 | 1124 | } | 
 | 1125 |  | 
 | 1126 | static void kgdbts_put_char(u8 chr) | 
 | 1127 | { | 
 | 1128 | 	if (ts.run_test) | 
 | 1129 | 		ts.run_test(0, chr); | 
 | 1130 | } | 
 | 1131 |  | 
 | 1132 | static int param_set_kgdbts_var(const char *kmessage, struct kernel_param *kp) | 
 | 1133 | { | 
 | 1134 | 	int len = strlen(kmessage); | 
 | 1135 |  | 
 | 1136 | 	if (len >= MAX_CONFIG_LEN) { | 
 | 1137 | 		printk(KERN_ERR "kgdbts: config string too long\n"); | 
 | 1138 | 		return -ENOSPC; | 
 | 1139 | 	} | 
 | 1140 |  | 
 | 1141 | 	/* Only copy in the string if the init function has not run yet */ | 
 | 1142 | 	if (configured < 0) { | 
 | 1143 | 		strcpy(config, kmessage); | 
 | 1144 | 		return 0; | 
 | 1145 | 	} | 
 | 1146 |  | 
| Dongdong Deng | 4dacd5c | 2010-08-30 21:06:00 -0500 | [diff] [blame] | 1147 | 	if (configured == 1) { | 
 | 1148 | 		printk(KERN_ERR "kgdbts: ERROR: Already configured and running.\n"); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1149 | 		return -EBUSY; | 
 | 1150 | 	} | 
 | 1151 |  | 
 | 1152 | 	strcpy(config, kmessage); | 
 | 1153 | 	/* Chop out \n char as a result of echo */ | 
 | 1154 | 	if (config[len - 1] == '\n') | 
 | 1155 | 		config[len - 1] = '\0'; | 
 | 1156 |  | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1157 | 	/* Go and configure with the new params. */ | 
 | 1158 | 	return configure_kgdbts(); | 
 | 1159 | } | 
 | 1160 |  | 
 | 1161 | static void kgdbts_pre_exp_handler(void) | 
 | 1162 | { | 
 | 1163 | 	/* Increment the module count when the debugger is active */ | 
 | 1164 | 	if (!kgdb_connected) | 
 | 1165 | 		try_module_get(THIS_MODULE); | 
 | 1166 | } | 
 | 1167 |  | 
 | 1168 | static void kgdbts_post_exp_handler(void) | 
 | 1169 | { | 
 | 1170 | 	/* decrement the module count when the debugger detaches */ | 
 | 1171 | 	if (!kgdb_connected) | 
 | 1172 | 		module_put(THIS_MODULE); | 
 | 1173 | } | 
 | 1174 |  | 
 | 1175 | static struct kgdb_io kgdbts_io_ops = { | 
 | 1176 | 	.name			= "kgdbts", | 
 | 1177 | 	.read_char		= kgdbts_get_char, | 
 | 1178 | 	.write_char		= kgdbts_put_char, | 
 | 1179 | 	.pre_exception		= kgdbts_pre_exp_handler, | 
 | 1180 | 	.post_exception		= kgdbts_post_exp_handler, | 
 | 1181 | }; | 
 | 1182 |  | 
 | 1183 | module_init(init_kgdbts); | 
| Jason Wessel | e8d31c2 | 2008-03-07 16:34:17 -0600 | [diff] [blame] | 1184 | module_param_call(kgdbts, param_set_kgdbts_var, param_get_string, &kps, 0644); | 
 | 1185 | MODULE_PARM_DESC(kgdbts, "<A|V1|V2>[F#|S#][N#]"); | 
 | 1186 | MODULE_DESCRIPTION("KGDB Test Suite"); | 
 | 1187 | MODULE_LICENSE("GPL"); | 
 | 1188 | MODULE_AUTHOR("Wind River Systems, Inc."); | 
 | 1189 |  |