diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/kgdbts.c | 73 |
1 files changed, 62 insertions, 11 deletions
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 3cad9fce8054..d087456ba089 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c | |||
@@ -142,7 +142,9 @@ static int arch_needs_sstep_emulation = 1; | |||
142 | #else | 142 | #else |
143 | static int arch_needs_sstep_emulation; | 143 | static int arch_needs_sstep_emulation; |
144 | #endif | 144 | #endif |
145 | static unsigned long cont_addr; | ||
145 | static unsigned long sstep_addr; | 146 | static unsigned long sstep_addr; |
147 | static int restart_from_top_after_write; | ||
146 | static int sstep_state; | 148 | static int sstep_state; |
147 | 149 | ||
148 | /* Storage for the registers, in GDB format. */ | 150 | /* Storage for the registers, in GDB format. */ |
@@ -190,7 +192,8 @@ static int kgdbts_unreg_thread(void *ptr) | |||
190 | */ | 192 | */ |
191 | while (!final_ack) | 193 | while (!final_ack) |
192 | msleep_interruptible(1500); | 194 | msleep_interruptible(1500); |
193 | 195 | /* Pause for any other threads to exit after final ack. */ | |
196 | msleep_interruptible(1000); | ||
194 | if (configured) | 197 | if (configured) |
195 | kgdb_unregister_io_module(&kgdbts_io_ops); | 198 | kgdb_unregister_io_module(&kgdbts_io_ops); |
196 | configured = 0; | 199 | configured = 0; |
@@ -312,13 +315,21 @@ static int check_and_rewind_pc(char *put_str, char *arg) | |||
312 | if (addr + BREAK_INSTR_SIZE == ip) | 315 | if (addr + BREAK_INSTR_SIZE == ip) |
313 | offset = -BREAK_INSTR_SIZE; | 316 | offset = -BREAK_INSTR_SIZE; |
314 | #endif | 317 | #endif |
315 | if (strcmp(arg, "silent") && ip + offset != addr) { | 318 | |
319 | if (arch_needs_sstep_emulation && sstep_addr && | ||
320 | ip + offset == sstep_addr && | ||
321 | ((!strcmp(arg, "sys_open") || !strcmp(arg, "do_fork")))) { | ||
322 | /* This is special case for emulated single step */ | ||
323 | v2printk("Emul: rewind hit single step bp\n"); | ||
324 | restart_from_top_after_write = 1; | ||
325 | } else if (strcmp(arg, "silent") && ip + offset != addr) { | ||
316 | eprintk("kgdbts: BP mismatch %lx expected %lx\n", | 326 | eprintk("kgdbts: BP mismatch %lx expected %lx\n", |
317 | ip + offset, addr); | 327 | ip + offset, addr); |
318 | return 1; | 328 | return 1; |
319 | } | 329 | } |
320 | /* Readjust the instruction pointer if needed */ | 330 | /* Readjust the instruction pointer if needed */ |
321 | ip += offset; | 331 | ip += offset; |
332 | cont_addr = ip; | ||
322 | #ifdef GDB_ADJUSTS_BREAK_OFFSET | 333 | #ifdef GDB_ADJUSTS_BREAK_OFFSET |
323 | instruction_pointer_set(&kgdbts_regs, ip); | 334 | instruction_pointer_set(&kgdbts_regs, ip); |
324 | #endif | 335 | #endif |
@@ -328,6 +339,8 @@ static int check_and_rewind_pc(char *put_str, char *arg) | |||
328 | static int check_single_step(char *put_str, char *arg) | 339 | static int check_single_step(char *put_str, char *arg) |
329 | { | 340 | { |
330 | unsigned long addr = lookup_addr(arg); | 341 | unsigned long addr = lookup_addr(arg); |
342 | static int matched_id; | ||
343 | |||
331 | /* | 344 | /* |
332 | * From an arch indepent point of view the instruction pointer | 345 | * From an arch indepent point of view the instruction pointer |
333 | * should be on a different instruction | 346 | * should be on a different instruction |
@@ -338,17 +351,28 @@ static int check_single_step(char *put_str, char *arg) | |||
338 | v2printk("Singlestep stopped at IP: %lx\n", | 351 | v2printk("Singlestep stopped at IP: %lx\n", |
339 | instruction_pointer(&kgdbts_regs)); | 352 | instruction_pointer(&kgdbts_regs)); |
340 | 353 | ||
341 | if (sstep_thread_id != cont_thread_id && !arch_needs_sstep_emulation) { | 354 | if (sstep_thread_id != cont_thread_id) { |
342 | /* | 355 | /* |
343 | * Ensure we stopped in the same thread id as before, else the | 356 | * Ensure we stopped in the same thread id as before, else the |
344 | * debugger should continue until the original thread that was | 357 | * debugger should continue until the original thread that was |
345 | * single stepped is scheduled again, emulating gdb's behavior. | 358 | * single stepped is scheduled again, emulating gdb's behavior. |
346 | */ | 359 | */ |
347 | v2printk("ThrID does not match: %lx\n", cont_thread_id); | 360 | v2printk("ThrID does not match: %lx\n", cont_thread_id); |
361 | if (arch_needs_sstep_emulation) { | ||
362 | if (matched_id && | ||
363 | instruction_pointer(&kgdbts_regs) != addr) | ||
364 | goto continue_test; | ||
365 | matched_id++; | ||
366 | ts.idx -= 2; | ||
367 | sstep_state = 0; | ||
368 | return 0; | ||
369 | } | ||
348 | cont_instead_of_sstep = 1; | 370 | cont_instead_of_sstep = 1; |
349 | ts.idx -= 4; | 371 | ts.idx -= 4; |
350 | return 0; | 372 | return 0; |
351 | } | 373 | } |
374 | continue_test: | ||
375 | matched_id = 0; | ||
352 | if (instruction_pointer(&kgdbts_regs) == addr) { | 376 | if (instruction_pointer(&kgdbts_regs) == addr) { |
353 | eprintk("kgdbts: SingleStep failed at %lx\n", | 377 | eprintk("kgdbts: SingleStep failed at %lx\n", |
354 | instruction_pointer(&kgdbts_regs)); | 378 | instruction_pointer(&kgdbts_regs)); |
@@ -390,6 +414,31 @@ static int got_break(char *put_str, char *arg) | |||
390 | return 1; | 414 | return 1; |
391 | } | 415 | } |
392 | 416 | ||
417 | static void get_cont_catch(char *arg) | ||
418 | { | ||
419 | /* Always send detach because the test is completed at this point */ | ||
420 | fill_get_buf("D"); | ||
421 | } | ||
422 | |||
423 | static int put_cont_catch(char *put_str, char *arg) | ||
424 | { | ||
425 | /* This is at the end of the test and we catch any and all input */ | ||
426 | v2printk("kgdbts: cleanup task: %lx\n", sstep_thread_id); | ||
427 | ts.idx--; | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | static int emul_reset(char *put_str, char *arg) | ||
432 | { | ||
433 | if (strncmp(put_str, "$OK", 3)) | ||
434 | return 1; | ||
435 | if (restart_from_top_after_write) { | ||
436 | restart_from_top_after_write = 0; | ||
437 | ts.idx = -1; | ||
438 | } | ||
439 | return 0; | ||
440 | } | ||
441 | |||
393 | static void emul_sstep_get(char *arg) | 442 | static void emul_sstep_get(char *arg) |
394 | { | 443 | { |
395 | if (!arch_needs_sstep_emulation) { | 444 | if (!arch_needs_sstep_emulation) { |
@@ -443,8 +492,7 @@ static int emul_sstep_put(char *put_str, char *arg) | |||
443 | v2printk("Stopped at IP: %lx\n", | 492 | v2printk("Stopped at IP: %lx\n", |
444 | instruction_pointer(&kgdbts_regs)); | 493 | instruction_pointer(&kgdbts_regs)); |
445 | /* Want to stop at IP + break instruction size by default */ | 494 | /* Want to stop at IP + break instruction size by default */ |
446 | sstep_addr = instruction_pointer(&kgdbts_regs) + | 495 | sstep_addr = cont_addr + BREAK_INSTR_SIZE; |
447 | BREAK_INSTR_SIZE; | ||
448 | break; | 496 | break; |
449 | case 2: | 497 | case 2: |
450 | if (strncmp(put_str, "$OK", 3)) { | 498 | if (strncmp(put_str, "$OK", 3)) { |
@@ -456,6 +504,9 @@ static int emul_sstep_put(char *put_str, char *arg) | |||
456 | if (strncmp(put_str, "$T0", 3)) { | 504 | if (strncmp(put_str, "$T0", 3)) { |
457 | eprintk("kgdbts: failed continue sstep\n"); | 505 | eprintk("kgdbts: failed continue sstep\n"); |
458 | return 1; | 506 | return 1; |
507 | } else { | ||
508 | char *ptr = &put_str[11]; | ||
509 | kgdb_hex2long(&ptr, &sstep_thread_id); | ||
459 | } | 510 | } |
460 | break; | 511 | break; |
461 | case 4: | 512 | case 4: |
@@ -558,13 +609,13 @@ static struct test_struct do_fork_test[] = { | |||
558 | { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ | 609 | { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ |
559 | { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */ | 610 | { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */ |
560 | { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */ | 611 | { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */ |
561 | { "write", "OK", write_regs }, /* Write registers */ | 612 | { "write", "OK", write_regs, emul_reset }, /* Write registers */ |
562 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ | 613 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ |
563 | { "g", "do_fork", NULL, check_single_step }, | 614 | { "g", "do_fork", NULL, check_single_step }, |
564 | { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ | 615 | { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ |
565 | { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ | 616 | { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ |
566 | { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ | 617 | { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ |
567 | { "", "" }, | 618 | { "", "", get_cont_catch, put_cont_catch }, |
568 | }; | 619 | }; |
569 | 620 | ||
570 | /* Test for hitting a breakpoint at sys_open for what ever the number | 621 | /* Test for hitting a breakpoint at sys_open for what ever the number |
@@ -576,13 +627,13 @@ static struct test_struct sys_open_test[] = { | |||
576 | { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ | 627 | { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ |
577 | { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */ | 628 | { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */ |
578 | { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */ | 629 | { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */ |
579 | { "write", "OK", write_regs }, /* Write registers */ | 630 | { "write", "OK", write_regs, emul_reset }, /* Write registers */ |
580 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ | 631 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ |
581 | { "g", "sys_open", NULL, check_single_step }, | 632 | { "g", "sys_open", NULL, check_single_step }, |
582 | { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ | 633 | { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ |
583 | { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ | 634 | { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ |
584 | { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ | 635 | { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ |
585 | { "", "" }, | 636 | { "", "", get_cont_catch, put_cont_catch }, |
586 | }; | 637 | }; |
587 | 638 | ||
588 | /* | 639 | /* |
@@ -725,8 +776,8 @@ static int run_simple_test(int is_get_char, int chr) | |||
725 | /* This callback is a put char which is when kgdb sends data to | 776 | /* This callback is a put char which is when kgdb sends data to |
726 | * this I/O module. | 777 | * this I/O module. |
727 | */ | 778 | */ |
728 | if (ts.tst[ts.idx].get[0] == '\0' && | 779 | if (ts.tst[ts.idx].get[0] == '\0' && ts.tst[ts.idx].put[0] == '\0' && |
729 | ts.tst[ts.idx].put[0] == '\0') { | 780 | !ts.tst[ts.idx].get_handler) { |
730 | eprintk("kgdbts: ERROR: beyond end of test on" | 781 | eprintk("kgdbts: ERROR: beyond end of test on" |
731 | " '%s' line %i\n", ts.name, ts.idx); | 782 | " '%s' line %i\n", ts.name, ts.idx); |
732 | return 0; | 783 | return 0; |