// SPDX-License-Identifier: GPL-2.0-only/* * kgdbts is a test suite for kgdb for the sole purpose of validating * that key pieces of the kgdb internals are working properly such as * HW/SW breakpoints, single stepping, and NMI. * * Created by: Jason Wessel <jason.wessel@windriver.com> * * Copyright (c) 2008 Wind River Systems, Inc. *//* Information about the kgdb test suite. * ------------------------------------- * * The kgdb test suite is designed as a KGDB I/O module which * simulates the communications that a debugger would have with kgdb. * The tests are broken up in to a line by line and referenced here as * a "get" which is kgdb requesting input and "put" which is kgdb * sending a response. * * The kgdb suite can be invoked from the kernel command line * arguments system or executed dynamically at run time. The test * suite uses the variable "kgdbts" to obtain the information about * which tests to run and to configure the verbosity level. The * following are the various characters you can use with the kgdbts= * line: * * When using the "kgdbts=" you only choose one of the following core * test types: * A = Run all the core tests silently * V1 = Run all the core tests with minimal output * V2 = Run all the core tests in debug mode * * You can also specify optional tests: * N## = Go to sleep with interrupts of for ## seconds * to test the HW NMI watchdog * F## = Break at kernel_clone for ## iterations * S## = Break at sys_open for ## iterations * I## = Run the single step test ## iterations * * NOTE: that the kernel_clone and sys_open tests are mutually exclusive. * * To invoke the kgdb test suite from boot you use a kernel start * argument as follows: * kgdbts=V1 kgdbwait * Or if you wanted to perform the NMI test for 6 seconds and kernel_clone * test for 100 forks, you could use: * kgdbts=V1N6F100 kgdbwait * * The test suite can also be invoked at run time with: * echo kgdbts=V1N6F100 > /sys/module/kgdbts/parameters/kgdbts * Or as another example: * echo kgdbts=V2 > /sys/module/kgdbts/parameters/kgdbts * * When developing a new kgdb arch specific implementation or * using these tests for the purpose of regression testing, * several invocations are required. * * 1) Boot with the test suite enabled by using the kernel arguments * "kgdbts=V1F100 kgdbwait" * ## If kgdb arch specific implementation has NMI use * "kgdbts=V1N6F100 * * 2) After the system boot run the basic test. * echo kgdbts=V1 > /sys/module/kgdbts/parameters/kgdbts * * 3) Run the concurrency tests. It is best to use n+1 * while loops where n is the number of cpus you have * in your system. The example below uses only two * loops. * * ## This tests break points on sys_open * while [ 1 ] ; do find / > /dev/null 2>&1 ; done & * while [ 1 ] ; do find / > /dev/null 2>&1 ; done & * echo kgdbts=V1S10000 > /sys/module/kgdbts/parameters/kgdbts * fg # and hit control-c * fg # and hit control-c * ## This tests break points on kernel_clone * while [ 1 ] ; do date > /dev/null ; done & * while [ 1 ] ; do date > /dev/null ; done & * echo kgdbts=V1F1000 > /sys/module/kgdbts/parameters/kgdbts * fg # and hit control-c * */ #include <linux/kernel.h>#include <linux/kgdb.h>#include <linux/ctype.h>#include <linux/uaccess.h>#include <linux/syscalls.h>#include <linux/nmi.h>#include <linux/delay.h>#include <linux/hex.h>#include <linux/kthread.h>#include <linux/module.h>#include <linux/sched/task.h>#include <linux/kallsyms.h> #include <asm/sections.h>#include <asm/rwonce.h> #define v1printk(a...) do { \ if (verbose) \ printk(KERN_INFO a); \} while (0)#define v2printk(a...) do { \ if (verbose > 1) { \ 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 struct kgdb_io kgdbts_io_ops;static char get_buf[BUFMAX];static int get_buf_cnt;static char put_buf[BUFMAX];static int put_buf_cnt;static char scratch_buf[BUFMAX];static int verbose;static int repeat_test;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 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;#elsestatic int arch_needs_sstep_emulation;#endifstatic 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. */static unsigned long kgdbts_gdb_regs[(NUMREGBYTES + sizeof(unsigned long) - 1) / sizeof(unsigned long)];static struct pt_regs kgdbts_regs; /* -1 = init not run yet, 0 = unconfigured, 1 = configured. */static int configured = -1; #ifdef CONFIG_KGDB_TESTS_BOOT_STRINGstatic char config[MAX_CONFIG_LEN] = CONFIG_KGDB_TESTS_BOOT_STRING;#elsestatic char config[MAX_CONFIG_LEN];#endifstatic struct kparam_string kps = { .string = config, .maxlen = MAX_CONFIG_LEN,}; static void fill_get_buf(char *buf); struct test_struct { char *get; char *put; void (*get_handler)(char *); int (*put_handler)(char *, char *);}; struct test_state { char *name; struct test_struct *tst; int idx; int (*run_test) (int, int); int (*validate_put) (char *);}; static struct test_state ts; static int kgdbts_unreg_thread(void *ptr){ /* Wait until the tests are complete and then ungresiter the I/O * driver. */ 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; return 0;} /* This is noinline such that it can be used for a single location to * place a breakpoint */static noinline void kgdbts_break_test(void){ v2printk("kgdbts: breakpoint complete\n");} /* * This is a cached wrapper for kallsyms_lookup_name(). * * The cache is a big win for several tests. For example it more the doubles * the cycles per second during the sys_open test. This is not theoretic, * the performance improvement shows up at human scale, especially when * testing using emulators. * * Obviously neither re-entrant nor thread-safe but that is OK since it * can only be called from the debug trap (and therefore all other CPUs * are halted). */static unsigned long lookup_addr(char *arg){ static char cached_arg[KSYM_NAME_LEN]; static unsigned long cached_addr; if (strcmp(arg, cached_arg)) { strscpy(cached_arg, arg, KSYM_NAME_LEN); cached_addr = kallsyms_lookup_name(arg); } return (unsigned long)dereference_function_descriptor( (void *)cached_addr);} static void break_helper(char *bp_type, char *arg, unsigned long vaddr){ unsigned long addr; if (arg) addr = lookup_addr(arg); else addr = vaddr; sprintf(scratch_buf, "%s,%lx,%i", bp_type, addr, BREAK_INSTR_SIZE); fill_get_buf(scratch_buf);} static void sw_break(char *arg){ break_helper(force_hwbrks ? "Z1" : "Z0", arg, 0);} static void sw_rem_break(char *arg){ break_helper(force_hwbrks ? "z1" : "z0", arg, 0);} static void hw_break(char *arg){ break_helper("Z1", arg, 0);} static void hw_rem_break(char *arg){ break_helper("z1", arg, 0);} static void hw_write_break(char *arg){ break_helper("Z2", arg, 0);} static void hw_rem_write_break(char *arg){ break_helper("z2", arg, 0);} static void hw_access_break(char *arg){ break_helper("Z4", arg, 0);} static void hw_rem_access_break(char *arg){ break_helper("z4", arg, 0);} static void hw_break_val_access(void){ READ_ONCE(hw_break_val);} 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); unsigned long ip; int offset = 0; kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, NUMREGBYTES); gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); ip = instruction_pointer(&kgdbts_regs); v2printk("Stopped at IP: %lx\n", ip);#ifdef GDB_ADJUSTS_BREAK_OFFSET /* On some arches, a breakpoint stop requires it to be decremented */ if (addr + BREAK_INSTR_SIZE == ip) offset = -BREAK_INSTR_SIZE;#endif if (arch_needs_sstep_emulation && sstep_addr && ip + offset == sstep_addr && ((!strcmp(arg, "do_sys_openat2") || !strcmp(arg, "kernel_clone")))) { /* 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 return 0;} 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 */ kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, NUMREGBYTES); 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)); return 1; } return 0;} static void write_regs(char *arg){ memset(scratch_buf, 0, sizeof(scratch_buf)); scratch_buf[0] = 'G'; pt_regs_to_gdb_regs(kgdbts_gdb_regs, &kgdbts_regs); kgdb_mem2hex((char *)kgdbts_gdb_regs, &scratch_buf[1], NUMREGBYTES); fill_get_buf(scratch_buf);} static void skip_back_repeat_test(char *arg){ int go_back = simple_strtol(arg, NULL, 10); repeat_test--; if (repeat_test <= 0) { ts.idx++; } else { if (repeat_test % 100 == 0) v1printk("kgdbts:RUN ... %d remaining\n", repeat_test); ts.idx -= go_back; } fill_get_buf(ts.tst[ts.idx].get);} static int got_break(char *put_str, char *arg){ test_complete = 1; if (!strncmp(put_str+1, arg, 2)) { if (!strncmp(arg, "T0", 2)) test_complete = 2; return 0; } 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) { case 0: v2printk("Emulate single step\n"); /* Start by looking at the current PC */ fill_get_buf("g"); break; case 1: /* set breakpoint */ break_helper("Z0", NULL, sstep_addr); break; case 2: /* Continue */ fill_get_buf("c"); break; case 3: /* Clear breakpoint */ break_helper("z0", NULL, sstep_addr); break; default: eprintk("kgdbts: ERROR failed sstep get emulation\n"); } sstep_state++;} static int emul_sstep_put(char *put_str, char *arg){ if (!arch_needs_sstep_emulation) { 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: /* validate the "g" packet to get the IP */ kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, NUMREGBYTES); gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); v2printk("Stopped at IP: %lx\n",Lines 1–500 of 1192. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * kgdbts is a test suite for kgdb for the sole purpose of validating * that key pieces of the kgdb internals are working properly such as * HW/SW breakpoints, single stepping, and NMI. * * Created by: Jason Wessel <jason.wessel@windriver.com> * * Copyright (c) 2008 Wind River Systems, Inc. */ /* Information about the kgdb test suite. * ------------------------------------- * * The kgdb test suite is designed as a KGDB I/O module which * simulates the communications that a debugger would have with kgdb. * The tests are broken up in to a line by line and referenced here as * a "get" which is kgdb requesting input and "put" which is kgdb * sending a response. * * The kgdb suite can be invoked from the kernel command line * arguments system or executed dynamically at run time. The test * suite uses the variable "kgdbts" to obtain the information about * which tests to run and to configure the verbosity level. The * following are the various characters you can use with the kgdbts= * line: * * When using the "kgdbts=" you only choose one of the following core * test types: * A = Run all the core tests silently * V1 = Run all the core tests with minimal output * V2 = Run all the core tests in debug mode * * You can also specify optional tests: * N## = Go to sleep with interrupts of for ## seconds * to test the HW NMI watchdog * F## = Break at kernel_clone for ## iterations * S## = Break at sys_open for ## iterations * I## = Run the single step test ## iterations * * NOTE: that the kernel_clone and sys_open tests are mutually exclusive. * * To invoke the kgdb test suite from boot you use a kernel start * argument as follows: * kgdbts=V1 kgdbwait * Or if you wanted to perform the NMI test for 6 seconds and kernel_clone * test for 100 forks, you could use: * kgdbts=V1N6F100 kgdbwait * * The test suite can also be invoked at run time with: * echo kgdbts=V1N6F100 > /sys/module/kgdbts/parameters/kgdbts * Or as another example: * echo kgdbts=V2 > /sys/module/kgdbts/parameters/kgdbts * * When developing a new kgdb arch specific implementation or * using these tests for the purpose of regression testing, * several invocations are required. * * 1) Boot with the test suite enabled by using the kernel arguments * "kgdbts=V1F100 kgdbwait" * ## If kgdb arch specific implementation has NMI use * "kgdbts=V1N6F100 * * 2) After the system boot run the basic test. * echo kgdbts=V1 > /sys/module/kgdbts/parameters/kgdbts * * 3) Run the concurrency tests. It is best to use n+1 * while loops where n is the number of cpus you have * in your system. The example below uses only two * loops. * * ## This tests break points on sys_open * while [ 1 ] ; do find / > /dev/null 2>&1 ; done & * while [ 1 ] ; do find / > /dev/null 2>&1 ; done & * echo kgdbts=V1S10000 > /sys/module/kgdbts/parameters/kgdbts * fg # and hit control-c * fg # and hit control-c * ## This tests break points on kernel_clone * while [ 1 ] ; do date > /dev/null ; done & * while [ 1 ] ; do date > /dev/null ; done & * echo kgdbts=V1F1000 > /sys/module/kgdbts/parameters/kgdbts * fg # and hit control-c * */ #include <linux/kernel.h> #include <linux/kgdb.h> #include <linux/ctype.h> #include <linux/uaccess.h> #include <linux/syscalls.h> #include <linux/nmi.h> #include <linux/delay.h> #include <linux/hex.h> #include <linux/kthread.h> #include <linux/module.h> #include <linux/sched/task.h> #include <linux/kallsyms.h> #include <asm/sections.h> #include <asm/rwonce.h> #define v1printk(a...) do { \ if (verbose) \ printk(KERN_INFO a); \ } while (0) #define v2printk(a...) do { \ if (verbose > 1) { \ 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 struct kgdb_io kgdbts_io_ops; static char get_buf[BUFMAX]; static int get_buf_cnt; static char put_buf[BUFMAX]; static int put_buf_cnt; static char scratch_buf[BUFMAX]; static int verbose; static int repeat_test; 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 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. */ static unsigned long kgdbts_gdb_regs[(NUMREGBYTES + sizeof(unsigned long) - 1) / sizeof(unsigned long)]; static struct pt_regs kgdbts_regs; /* -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 fill_get_buf(char *buf); struct test_struct { char *get; char *put; void (*get_handler)(char *); int (*put_handler)(char *, char *); }; struct test_state { char *name; struct test_struct *tst; int idx; int (*run_test) (int, int); int (*validate_put) (char *); }; static struct test_state ts; static int kgdbts_unreg_thread(void *ptr) { /* Wait until the tests are complete and then ungresiter the I/O * driver. */ 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; return 0; } /* This is noinline such that it can be used for a single location to * place a breakpoint */ static noinline void kgdbts_break_test(void) { v2printk("kgdbts: breakpoint complete\n"); } /* * This is a cached wrapper for kallsyms_lookup_name(). * * The cache is a big win for several tests. For example it more the doubles * the cycles per second during the sys_open test. This is not theoretic, * the performance improvement shows up at human scale, especially when * testing using emulators. * * Obviously neither re-entrant nor thread-safe but that is OK since it * can only be called from the debug trap (and therefore all other CPUs * are halted). */ static unsigned long lookup_addr(char *arg) { static char cached_arg[KSYM_NAME_LEN]; static unsigned long cached_addr; if (strcmp(arg, cached_arg)) { strscpy(cached_arg, arg, KSYM_NAME_LEN); cached_addr = kallsyms_lookup_name(arg); } return (unsigned long)dereference_function_descriptor( (void *)cached_addr); } static void break_helper(char *bp_type, char *arg, unsigned long vaddr) { unsigned long addr; if (arg) addr = lookup_addr(arg); else addr = vaddr; sprintf(scratch_buf, "%s,%lx,%i", bp_type, addr, BREAK_INSTR_SIZE); fill_get_buf(scratch_buf); } static void sw_break(char *arg) { break_helper(force_hwbrks ? "Z1" : "Z0", arg, 0); } static void sw_rem_break(char *arg) { break_helper(force_hwbrks ? "z1" : "z0", arg, 0); } static void hw_break(char *arg) { break_helper("Z1", arg, 0); } static void hw_rem_break(char *arg) { break_helper("z1", arg, 0); } static void hw_write_break(char *arg) { break_helper("Z2", arg, 0); } static void hw_rem_write_break(char *arg) { break_helper("z2", arg, 0); } static void hw_access_break(char *arg) { break_helper("Z4", arg, 0); } static void hw_rem_access_break(char *arg) { break_helper("z4", arg, 0); } static void hw_break_val_access(void) { READ_ONCE(hw_break_val); } 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); unsigned long ip; int offset = 0; kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, NUMREGBYTES); gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); ip = instruction_pointer(&kgdbts_regs); v2printk("Stopped at IP: %lx\n", ip); #ifdef GDB_ADJUSTS_BREAK_OFFSET /* On some arches, a breakpoint stop requires it to be decremented */ if (addr + BREAK_INSTR_SIZE == ip) offset = -BREAK_INSTR_SIZE; #endif if (arch_needs_sstep_emulation && sstep_addr && ip + offset == sstep_addr && ((!strcmp(arg, "do_sys_openat2") || !strcmp(arg, "kernel_clone")))) { /* 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 return 0; } 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 */ kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, NUMREGBYTES); 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)); return 1; } return 0; } static void write_regs(char *arg) { memset(scratch_buf, 0, sizeof(scratch_buf)); scratch_buf[0] = 'G'; pt_regs_to_gdb_regs(kgdbts_gdb_regs, &kgdbts_regs); kgdb_mem2hex((char *)kgdbts_gdb_regs, &scratch_buf[1], NUMREGBYTES); fill_get_buf(scratch_buf); } static void skip_back_repeat_test(char *arg) { int go_back = simple_strtol(arg, NULL, 10); repeat_test--; if (repeat_test <= 0) { ts.idx++; } else { if (repeat_test % 100 == 0) v1printk("kgdbts:RUN ... %d remaining\n", repeat_test); ts.idx -= go_back; } fill_get_buf(ts.tst[ts.idx].get); } static int got_break(char *put_str, char *arg) { test_complete = 1; if (!strncmp(put_str+1, arg, 2)) { if (!strncmp(arg, "T0", 2)) test_complete = 2; return 0; } 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) { case 0: v2printk("Emulate single step\n"); /* Start by looking at the current PC */ fill_get_buf("g"); break; case 1: /* set breakpoint */ break_helper("Z0", NULL, sstep_addr); break; case 2: /* Continue */ fill_get_buf("c"); break; case 3: /* Clear breakpoint */ break_helper("z0", NULL, sstep_addr); break; default: eprintk("kgdbts: ERROR failed sstep get emulation\n"); } sstep_state++; } static int emul_sstep_put(char *put_str, char *arg) { if (!arch_needs_sstep_emulation) { 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: /* validate the "g" packet to get the IP */ kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs, NUMREGBYTES); gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); v2printk("Stopped at IP: %lx\n",