diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/misc/kgdbts.c | 54 |
1 files changed, 43 insertions, 11 deletions
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 997e94d618b..3cad9fce805 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c | |||
@@ -134,6 +134,9 @@ static int force_hwbrks; | |||
134 | static int hwbreaks_ok; | 134 | static int hwbreaks_ok; |
135 | static int hw_break_val; | 135 | static int hw_break_val; |
136 | static int hw_break_val2; | 136 | static int hw_break_val2; |
137 | static int cont_instead_of_sstep; | ||
138 | static unsigned long cont_thread_id; | ||
139 | static unsigned long sstep_thread_id; | ||
137 | #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC) | 140 | #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_SPARC) |
138 | static int arch_needs_sstep_emulation = 1; | 141 | static int arch_needs_sstep_emulation = 1; |
139 | #else | 142 | #else |
@@ -211,7 +214,7 @@ static unsigned long lookup_addr(char *arg) | |||
211 | if (!strcmp(arg, "kgdbts_break_test")) | 214 | if (!strcmp(arg, "kgdbts_break_test")) |
212 | addr = (unsigned long)kgdbts_break_test; | 215 | addr = (unsigned long)kgdbts_break_test; |
213 | else if (!strcmp(arg, "sys_open")) | 216 | else if (!strcmp(arg, "sys_open")) |
214 | addr = (unsigned long)sys_open; | 217 | addr = (unsigned long)do_sys_open; |
215 | else if (!strcmp(arg, "do_fork")) | 218 | else if (!strcmp(arg, "do_fork")) |
216 | addr = (unsigned long)do_fork; | 219 | addr = (unsigned long)do_fork; |
217 | else if (!strcmp(arg, "hw_break_val")) | 220 | else if (!strcmp(arg, "hw_break_val")) |
@@ -283,6 +286,16 @@ static void hw_break_val_write(void) | |||
283 | hw_break_val++; | 286 | hw_break_val++; |
284 | } | 287 | } |
285 | 288 | ||
289 | static int get_thread_id_continue(char *put_str, char *arg) | ||
290 | { | ||
291 | char *ptr = &put_str[11]; | ||
292 | |||
293 | if (put_str[1] != 'T' || put_str[2] != '0') | ||
294 | return 1; | ||
295 | kgdb_hex2long(&ptr, &cont_thread_id); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
286 | static int check_and_rewind_pc(char *put_str, char *arg) | 299 | static int check_and_rewind_pc(char *put_str, char *arg) |
287 | { | 300 | { |
288 | unsigned long addr = lookup_addr(arg); | 301 | unsigned long addr = lookup_addr(arg); |
@@ -324,6 +337,18 @@ static int check_single_step(char *put_str, char *arg) | |||
324 | gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); | 337 | gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs); |
325 | v2printk("Singlestep stopped at IP: %lx\n", | 338 | v2printk("Singlestep stopped at IP: %lx\n", |
326 | instruction_pointer(&kgdbts_regs)); | 339 | instruction_pointer(&kgdbts_regs)); |
340 | |||
341 | if (sstep_thread_id != cont_thread_id && !arch_needs_sstep_emulation) { | ||
342 | /* | ||
343 | * Ensure we stopped in the same thread id as before, else the | ||
344 | * debugger should continue until the original thread that was | ||
345 | * single stepped is scheduled again, emulating gdb's behavior. | ||
346 | */ | ||
347 | v2printk("ThrID does not match: %lx\n", cont_thread_id); | ||
348 | cont_instead_of_sstep = 1; | ||
349 | ts.idx -= 4; | ||
350 | return 0; | ||
351 | } | ||
327 | if (instruction_pointer(&kgdbts_regs) == addr) { | 352 | if (instruction_pointer(&kgdbts_regs) == addr) { |
328 | eprintk("kgdbts: SingleStep failed at %lx\n", | 353 | eprintk("kgdbts: SingleStep failed at %lx\n", |
329 | instruction_pointer(&kgdbts_regs)); | 354 | instruction_pointer(&kgdbts_regs)); |
@@ -368,7 +393,12 @@ static int got_break(char *put_str, char *arg) | |||
368 | static void emul_sstep_get(char *arg) | 393 | static void emul_sstep_get(char *arg) |
369 | { | 394 | { |
370 | if (!arch_needs_sstep_emulation) { | 395 | if (!arch_needs_sstep_emulation) { |
371 | fill_get_buf(arg); | 396 | if (cont_instead_of_sstep) { |
397 | cont_instead_of_sstep = 0; | ||
398 | fill_get_buf("c"); | ||
399 | } else { | ||
400 | fill_get_buf(arg); | ||
401 | } | ||
372 | return; | 402 | return; |
373 | } | 403 | } |
374 | switch (sstep_state) { | 404 | switch (sstep_state) { |
@@ -398,9 +428,11 @@ static void emul_sstep_get(char *arg) | |||
398 | static int emul_sstep_put(char *put_str, char *arg) | 428 | static int emul_sstep_put(char *put_str, char *arg) |
399 | { | 429 | { |
400 | if (!arch_needs_sstep_emulation) { | 430 | if (!arch_needs_sstep_emulation) { |
401 | if (!strncmp(put_str+1, arg, 2)) | 431 | char *ptr = &put_str[11]; |
402 | return 0; | 432 | if (put_str[1] != 'T' || put_str[2] != '0') |
403 | return 1; | 433 | return 1; |
434 | kgdb_hex2long(&ptr, &sstep_thread_id); | ||
435 | return 0; | ||
404 | } | 436 | } |
405 | switch (sstep_state) { | 437 | switch (sstep_state) { |
406 | case 1: | 438 | case 1: |
@@ -502,10 +534,10 @@ static struct test_struct bad_read_test[] = { | |||
502 | static struct test_struct singlestep_break_test[] = { | 534 | static struct test_struct singlestep_break_test[] = { |
503 | { "?", "S0*" }, /* Clear break points */ | 535 | { "?", "S0*" }, /* Clear break points */ |
504 | { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ | 536 | { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ |
505 | { "c", "T0*", }, /* Continue */ | 537 | { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ |
538 | { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */ | ||
506 | { "g", "kgdbts_break_test", NULL, check_and_rewind_pc }, | 539 | { "g", "kgdbts_break_test", NULL, check_and_rewind_pc }, |
507 | { "write", "OK", write_regs }, /* Write registers */ | 540 | { "write", "OK", write_regs }, /* Write registers */ |
508 | { "kgdbts_break_test", "OK", sw_rem_break }, /*remove breakpoint */ | ||
509 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ | 541 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ |
510 | { "g", "kgdbts_break_test", NULL, check_single_step }, | 542 | { "g", "kgdbts_break_test", NULL, check_single_step }, |
511 | { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ | 543 | { "kgdbts_break_test", "OK", sw_break, }, /* set sw breakpoint */ |
@@ -523,10 +555,10 @@ static struct test_struct singlestep_break_test[] = { | |||
523 | static struct test_struct do_fork_test[] = { | 555 | static struct test_struct do_fork_test[] = { |
524 | { "?", "S0*" }, /* Clear break points */ | 556 | { "?", "S0*" }, /* Clear break points */ |
525 | { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ | 557 | { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ |
526 | { "c", "T0*", }, /* Continue */ | 558 | { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ |
559 | { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */ | ||
527 | { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */ | 560 | { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */ |
528 | { "write", "OK", write_regs }, /* Write registers */ | 561 | { "write", "OK", write_regs }, /* Write registers */ |
529 | { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */ | ||
530 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ | 562 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ |
531 | { "g", "do_fork", NULL, check_single_step }, | 563 | { "g", "do_fork", NULL, check_single_step }, |
532 | { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ | 564 | { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ |
@@ -541,10 +573,10 @@ static struct test_struct do_fork_test[] = { | |||
541 | static struct test_struct sys_open_test[] = { | 573 | static struct test_struct sys_open_test[] = { |
542 | { "?", "S0*" }, /* Clear break points */ | 574 | { "?", "S0*" }, /* Clear break points */ |
543 | { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ | 575 | { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ |
544 | { "c", "T0*", }, /* Continue */ | 576 | { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ |
577 | { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */ | ||
545 | { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */ | 578 | { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */ |
546 | { "write", "OK", write_regs }, /* Write registers */ | 579 | { "write", "OK", write_regs }, /* Write registers */ |
547 | { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */ | ||
548 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ | 580 | { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ |
549 | { "g", "sys_open", NULL, check_single_step }, | 581 | { "g", "sys_open", NULL, check_single_step }, |
550 | { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ | 582 | { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ |