libata: disable LPM for Crucial BX100 SSD 500GB drive
[pandora-kernel.git] / drivers / misc / kgdbts.c
index 8cebec5..3aa9a96 100644 (file)
 #include <linux/nmi.h>
 #include <linux/delay.h>
 #include <linux/kthread.h>
+#include <linux/module.h>
 
 #define v1printk(a...) do { \
        if (verbose) \
@@ -133,12 +134,17 @@ static int force_hwbrks;
 static int hwbreaks_ok;
 static int hw_break_val;
 static int hw_break_val2;
+static int cont_instead_of_sstep;
+static unsigned long cont_thread_id;
+static unsigned long sstep_thread_id;
 #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC)
 static int arch_needs_sstep_emulation = 1;
 #else
 static int arch_needs_sstep_emulation;
 #endif
+static unsigned long cont_addr;
 static unsigned long sstep_addr;
+static int restart_from_top_after_write;
 static int sstep_state;
 
 /* Storage for the registers, in GDB format. */
@@ -186,7 +192,8 @@ static int kgdbts_unreg_thread(void *ptr)
         */
        while (!final_ack)
                msleep_interruptible(1500);
-
+       /* Pause for any other threads to exit after final ack. */
+       msleep_interruptible(1000);
        if (configured)
                kgdb_unregister_io_module(&kgdbts_io_ops);
        configured = 0;
@@ -210,7 +217,7 @@ static unsigned long lookup_addr(char *arg)
        if (!strcmp(arg, "kgdbts_break_test"))
                addr = (unsigned long)kgdbts_break_test;
        else if (!strcmp(arg, "sys_open"))
-               addr = (unsigned long)sys_open;
+               addr = (unsigned long)do_sys_open;
        else if (!strcmp(arg, "do_fork"))
                addr = (unsigned long)do_fork;
        else if (!strcmp(arg, "hw_break_val"))
@@ -282,6 +289,16 @@ static void hw_break_val_write(void)
        hw_break_val++;
 }
 
+static int get_thread_id_continue(char *put_str, char *arg)
+{
+       char *ptr = &put_str[11];
+
+       if (put_str[1] != 'T' || put_str[2] != '0')
+               return 1;
+       kgdb_hex2long(&ptr, &cont_thread_id);
+       return 0;
+}
+
 static int check_and_rewind_pc(char *put_str, char *arg)
 {
        unsigned long addr = lookup_addr(arg);
@@ -298,13 +315,21 @@ static int check_and_rewind_pc(char *put_str, char *arg)
        if (addr + BREAK_INSTR_SIZE == ip)
                offset = -BREAK_INSTR_SIZE;
 #endif
-       if (strcmp(arg, "silent") && ip + offset != addr) {
+
+       if (arch_needs_sstep_emulation && sstep_addr &&
+           ip + offset == sstep_addr &&
+           ((!strcmp(arg, "sys_open") || !strcmp(arg, "do_fork")))) {
+               /* This is special case for emulated single step */
+               v2printk("Emul: rewind hit single step bp\n");
+               restart_from_top_after_write = 1;
+       } else if (strcmp(arg, "silent") && ip + offset != addr) {
                eprintk("kgdbts: BP mismatch %lx expected %lx\n",
                           ip + offset, addr);
                return 1;
        }
        /* Readjust the instruction pointer if needed */
        ip += offset;
+       cont_addr = ip;
 #ifdef GDB_ADJUSTS_BREAK_OFFSET
        instruction_pointer_set(&kgdbts_regs, ip);
 #endif
@@ -314,6 +339,8 @@ static int check_and_rewind_pc(char *put_str, char *arg)
 static int check_single_step(char *put_str, char *arg)
 {
        unsigned long addr = lookup_addr(arg);
+       static int matched_id;
+
        /*
         * From an arch indepent point of view the instruction pointer
         * should be on a different instruction
@@ -323,6 +350,29 @@ static int check_single_step(char *put_str, char *arg)
        gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs);
        v2printk("Singlestep stopped at IP: %lx\n",
                   instruction_pointer(&kgdbts_regs));
+
+       if (sstep_thread_id != cont_thread_id) {
+               /*
+                * Ensure we stopped in the same thread id as before, else the
+                * debugger should continue until the original thread that was
+                * single stepped is scheduled again, emulating gdb's behavior.
+                */
+               v2printk("ThrID does not match: %lx\n", cont_thread_id);
+               if (arch_needs_sstep_emulation) {
+                       if (matched_id &&
+                           instruction_pointer(&kgdbts_regs) != addr)
+                               goto continue_test;
+                       matched_id++;
+                       ts.idx -= 2;
+                       sstep_state = 0;
+                       return 0;
+               }
+               cont_instead_of_sstep = 1;
+               ts.idx -= 4;
+               return 0;
+       }
+continue_test:
+       matched_id = 0;
        if (instruction_pointer(&kgdbts_regs) == addr) {
                eprintk("kgdbts: SingleStep failed at %lx\n",
                           instruction_pointer(&kgdbts_regs));
@@ -364,10 +414,40 @@ static int got_break(char *put_str, char *arg)
        return 1;
 }
 
+static void get_cont_catch(char *arg)
+{
+       /* Always send detach because the test is completed at this point */
+       fill_get_buf("D");
+}
+
+static int put_cont_catch(char *put_str, char *arg)
+{
+       /* This is at the end of the test and we catch any and all input */
+       v2printk("kgdbts: cleanup task: %lx\n", sstep_thread_id);
+       ts.idx--;
+       return 0;
+}
+
+static int emul_reset(char *put_str, char *arg)
+{
+       if (strncmp(put_str, "$OK", 3))
+               return 1;
+       if (restart_from_top_after_write) {
+               restart_from_top_after_write = 0;
+               ts.idx = -1;
+       }
+       return 0;
+}
+
 static void emul_sstep_get(char *arg)
 {
        if (!arch_needs_sstep_emulation) {
-               fill_get_buf(arg);
+               if (cont_instead_of_sstep) {
+                       cont_instead_of_sstep = 0;
+                       fill_get_buf("c");
+               } else {
+                       fill_get_buf(arg);
+               }
                return;
        }
        switch (sstep_state) {
@@ -397,9 +477,11 @@ static void emul_sstep_get(char *arg)
 static int emul_sstep_put(char *put_str, char *arg)
 {
        if (!arch_needs_sstep_emulation) {
-               if (!strncmp(put_str+1, arg, 2))
-                       return 0;
-               return 1;
+               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:
@@ -410,8 +492,7 @@ static int emul_sstep_put(char *put_str, char *arg)
                v2printk("Stopped at IP: %lx\n",
                         instruction_pointer(&kgdbts_regs));
                /* Want to stop at IP + break instruction size by default */
-               sstep_addr = instruction_pointer(&kgdbts_regs) +
-                       BREAK_INSTR_SIZE;
+               sstep_addr = cont_addr + BREAK_INSTR_SIZE;
                break;
        case 2:
                if (strncmp(put_str, "$OK", 3)) {
@@ -423,6 +504,9 @@ static int emul_sstep_put(char *put_str, char *arg)
                if (strncmp(put_str, "$T0", 3)) {
                        eprintk("kgdbts: failed continue sstep\n");
                        return 1;
+               } else {
+                       char *ptr = &put_str[11];
+                       kgdb_hex2long(&ptr, &sstep_thread_id);
                }
                break;
        case 4:
@@ -501,10 +585,10 @@ static struct test_struct bad_read_test[] = {
 static struct test_struct singlestep_break_test[] = {
        { "?", "S0*" }, /* Clear break points */
        { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
-       { "c", "T0*", }, /* Continue */
+       { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
+       { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */
        { "g", "kgdbts_break_test", NULL, check_and_rewind_pc },
        { "write", "OK", write_regs }, /* Write registers */
-       { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */
        { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
        { "g", "kgdbts_break_test", NULL, check_single_step },
        { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */
@@ -522,16 +606,16 @@ static struct test_struct singlestep_break_test[] = {
 static struct test_struct do_fork_test[] = {
        { "?", "S0*" }, /* Clear break points */
        { "do_fork", "OK", sw_break, }, /* set sw breakpoint */
-       { "c", "T0*", }, /* Continue */
-       { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */
-       { "write", "OK", write_regs }, /* Write registers */
+       { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
        { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */
+       { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */
+       { "write", "OK", write_regs, emul_reset }, /* Write registers */
        { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
        { "g", "do_fork", NULL, check_single_step },
        { "do_fork", "OK", sw_break, }, /* set sw breakpoint */
        { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */
        { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */
-       { "", "" },
+       { "", "", get_cont_catch, put_cont_catch },
 };
 
 /* Test for hitting a breakpoint at sys_open for what ever the number
@@ -540,16 +624,16 @@ static struct test_struct do_fork_test[] = {
 static struct test_struct sys_open_test[] = {
        { "?", "S0*" }, /* Clear break points */
        { "sys_open", "OK", sw_break, }, /* set sw breakpoint */
-       { "c", "T0*", }, /* Continue */
-       { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */
-       { "write", "OK", write_regs }, /* Write registers */
+       { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
        { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */
+       { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */
+       { "write", "OK", write_regs, emul_reset }, /* Write registers */
        { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
        { "g", "sys_open", NULL, check_single_step },
        { "sys_open", "OK", sw_break, }, /* set sw breakpoint */
        { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */
        { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */
-       { "", "" },
+       { "", "", get_cont_catch, put_cont_catch },
 };
 
 /*
@@ -692,8 +776,8 @@ static int run_simple_test(int is_get_char, int chr)
        /* This callback is a put char which is when kgdb sends data to
         * this I/O module.
         */
-       if (ts.tst[ts.idx].get[0] == '\0' &&
-               ts.tst[ts.idx].put[0] == '\0') {
+       if (ts.tst[ts.idx].get[0] == '\0' && ts.tst[ts.idx].put[0] == '\0' &&
+           !ts.tst[ts.idx].get_handler) {
                eprintk("kgdbts: ERROR: beyond end of test on"
                           " '%s' line %i\n", ts.name, ts.idx);
                return 0;
@@ -906,6 +990,17 @@ static void kgdbts_run_tests(void)
        if (ptr)
                sstep_test = simple_strtol(ptr+1, NULL, 10);
 
+       /* All HW break point tests */
+       if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) {
+               hwbreaks_ok = 1;
+               v1printk("kgdbts:RUN hw breakpoint test\n");
+               run_breakpoint_test(1);
+               v1printk("kgdbts:RUN hw write breakpoint test\n");
+               run_hw_break_test(1);
+               v1printk("kgdbts:RUN access write breakpoint test\n");
+               run_hw_break_test(0);
+       }
+
        /* required internal KGDB tests */
        v1printk("kgdbts:RUN plant and detach test\n");
        run_plant_and_detach_test(0);
@@ -923,35 +1018,11 @@ static void kgdbts_run_tests(void)
 
        /* ===Optional tests=== */
 
-       /* All HW break point tests */
-       if (arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT) {
-               hwbreaks_ok = 1;
-               v1printk("kgdbts:RUN hw breakpoint test\n");
-               run_breakpoint_test(1);
-               v1printk("kgdbts:RUN hw write breakpoint test\n");
-               run_hw_break_test(1);
-               v1printk("kgdbts:RUN access write breakpoint test\n");
-               run_hw_break_test(0);
-       }
-
        if (nmi_sleep) {
                v1printk("kgdbts:RUN NMI sleep %i seconds test\n", nmi_sleep);
                run_nmi_sleep_test(nmi_sleep);
        }
 
-#ifdef CONFIG_DEBUG_RODATA
-       /* Until there is an api to write to read-only text segments, use
-        * HW breakpoints for the remainder of any tests, else print a
-        * failure message if hw breakpoints do not work.
-        */
-       if (!(arch_kgdb_ops.flags & KGDB_HW_BREAKPOINT && hwbreaks_ok)) {
-               eprintk("kgdbts: HW breakpoints do not work,"
-                       "skipping remaining tests\n");
-               return;
-       }
-       force_hwbrks = 1;
-#endif /* CONFIG_DEBUG_RODATA */
-
        /* If the do_fork test is run it will be the last test that is
         * executed because a kernel thread will be spawned at the very
         * end to unregister the debug hooks.