diff options
Diffstat (limited to 'arch/arm/kernel/ptrace.c')
| -rw-r--r-- | arch/arm/kernel/ptrace.c | 113 |
1 files changed, 47 insertions, 66 deletions
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index a2ea3854cb3c..3f562a7c0a99 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c | |||
| @@ -452,12 +452,23 @@ void ptrace_cancel_bpt(struct task_struct *child) | |||
| 452 | clear_breakpoint(child, &child->thread.debug.bp[i]); | 452 | clear_breakpoint(child, &child->thread.debug.bp[i]); |
| 453 | } | 453 | } |
| 454 | 454 | ||
| 455 | void user_disable_single_step(struct task_struct *task) | ||
| 456 | { | ||
| 457 | task->ptrace &= ~PT_SINGLESTEP; | ||
| 458 | ptrace_cancel_bpt(task); | ||
| 459 | } | ||
| 460 | |||
| 461 | void user_enable_single_step(struct task_struct *task) | ||
| 462 | { | ||
| 463 | task->ptrace |= PT_SINGLESTEP; | ||
| 464 | } | ||
| 465 | |||
| 455 | /* | 466 | /* |
| 456 | * Called by kernel/ptrace.c when detaching.. | 467 | * Called by kernel/ptrace.c when detaching.. |
| 457 | */ | 468 | */ |
| 458 | void ptrace_disable(struct task_struct *child) | 469 | void ptrace_disable(struct task_struct *child) |
| 459 | { | 470 | { |
| 460 | single_step_disable(child); | 471 | user_disable_single_step(child); |
| 461 | } | 472 | } |
| 462 | 473 | ||
| 463 | /* | 474 | /* |
| @@ -499,10 +510,41 @@ static struct undef_hook thumb_break_hook = { | |||
| 499 | .fn = break_trap, | 510 | .fn = break_trap, |
| 500 | }; | 511 | }; |
| 501 | 512 | ||
| 513 | static int thumb2_break_trap(struct pt_regs *regs, unsigned int instr) | ||
| 514 | { | ||
| 515 | unsigned int instr2; | ||
| 516 | void __user *pc; | ||
| 517 | |||
| 518 | /* Check the second half of the instruction. */ | ||
| 519 | pc = (void __user *)(instruction_pointer(regs) + 2); | ||
| 520 | |||
| 521 | if (processor_mode(regs) == SVC_MODE) { | ||
| 522 | instr2 = *(u16 *) pc; | ||
| 523 | } else { | ||
| 524 | get_user(instr2, (u16 __user *)pc); | ||
| 525 | } | ||
| 526 | |||
| 527 | if (instr2 == 0xa000) { | ||
| 528 | ptrace_break(current, regs); | ||
| 529 | return 0; | ||
| 530 | } else { | ||
| 531 | return 1; | ||
| 532 | } | ||
| 533 | } | ||
| 534 | |||
| 535 | static struct undef_hook thumb2_break_hook = { | ||
| 536 | .instr_mask = 0xffff, | ||
| 537 | .instr_val = 0xf7f0, | ||
| 538 | .cpsr_mask = PSR_T_BIT, | ||
| 539 | .cpsr_val = PSR_T_BIT, | ||
| 540 | .fn = thumb2_break_trap, | ||
| 541 | }; | ||
| 542 | |||
| 502 | static int __init ptrace_break_init(void) | 543 | static int __init ptrace_break_init(void) |
| 503 | { | 544 | { |
| 504 | register_undef_hook(&arm_break_hook); | 545 | register_undef_hook(&arm_break_hook); |
| 505 | register_undef_hook(&thumb_break_hook); | 546 | register_undef_hook(&thumb_break_hook); |
| 547 | register_undef_hook(&thumb2_break_hook); | ||
| 506 | return 0; | 548 | return 0; |
| 507 | } | 549 | } |
| 508 | 550 | ||
| @@ -669,7 +711,7 @@ static int ptrace_getvfpregs(struct task_struct *tsk, void __user *data) | |||
| 669 | union vfp_state *vfp = &thread->vfpstate; | 711 | union vfp_state *vfp = &thread->vfpstate; |
| 670 | struct user_vfp __user *ufp = data; | 712 | struct user_vfp __user *ufp = data; |
| 671 | 713 | ||
| 672 | vfp_sync_state(thread); | 714 | vfp_sync_hwstate(thread); |
| 673 | 715 | ||
| 674 | /* copy the floating point registers */ | 716 | /* copy the floating point registers */ |
| 675 | if (copy_to_user(&ufp->fpregs, &vfp->hard.fpregs, | 717 | if (copy_to_user(&ufp->fpregs, &vfp->hard.fpregs, |
| @@ -692,7 +734,7 @@ static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data) | |||
| 692 | union vfp_state *vfp = &thread->vfpstate; | 734 | union vfp_state *vfp = &thread->vfpstate; |
| 693 | struct user_vfp __user *ufp = data; | 735 | struct user_vfp __user *ufp = data; |
| 694 | 736 | ||
| 695 | vfp_sync_state(thread); | 737 | vfp_sync_hwstate(thread); |
| 696 | 738 | ||
| 697 | /* copy the floating point registers */ | 739 | /* copy the floating point registers */ |
| 698 | if (copy_from_user(&vfp->hard.fpregs, &ufp->fpregs, | 740 | if (copy_from_user(&vfp->hard.fpregs, &ufp->fpregs, |
| @@ -703,6 +745,8 @@ static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data) | |||
| 703 | if (get_user(vfp->hard.fpscr, &ufp->fpscr)) | 745 | if (get_user(vfp->hard.fpscr, &ufp->fpscr)) |
| 704 | return -EFAULT; | 746 | return -EFAULT; |
| 705 | 747 | ||
| 748 | vfp_flush_hwstate(thread); | ||
| 749 | |||
| 706 | return 0; | 750 | return 0; |
| 707 | } | 751 | } |
| 708 | #endif | 752 | #endif |
| @@ -712,77 +756,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
| 712 | int ret; | 756 | int ret; |
| 713 | 757 | ||
| 714 | switch (request) { | 758 | switch (request) { |
| 715 | /* | ||
| 716 | * read word at location "addr" in the child process. | ||
| 717 | */ | ||
| 718 | case PTRACE_PEEKTEXT: | ||
| 719 | case PTRACE_PEEKDATA: | ||
| 720 | ret = generic_ptrace_peekdata(child, addr, data); | ||
| 721 | break; | ||
| 722 | |||
| 723 | case PTRACE_PEEKUSR: | 759 | case PTRACE_PEEKUSR: |
| 724 | ret = ptrace_read_user(child, addr, (unsigned long __user *)data); | 760 | ret = ptrace_read_user(child, addr, (unsigned long __user *)data); |
| 725 | break; | 761 | break; |
| 726 | 762 | ||
| 727 | /* | ||
| 728 | * write the word at location addr. | ||
| 729 | */ | ||
| 730 | case PTRACE_POKETEXT: | ||
| 731 | case PTRACE_POKEDATA: | ||
| 732 | ret = generic_ptrace_pokedata(child, addr, data); | ||
| 733 | break; | ||
| 734 | |||
| 735 | case PTRACE_POKEUSR: | 763 | case PTRACE_POKEUSR: |
| 736 | ret = ptrace_write_user(child, addr, data); | 764 | ret = ptrace_write_user(child, addr, data); |
| 737 | break; | 765 | break; |
| 738 | 766 | ||
| 739 | /* | ||
| 740 | * continue/restart and stop at next (return from) syscall | ||
| 741 | */ | ||
| 742 | case PTRACE_SYSCALL: | ||
| 743 | case PTRACE_CONT: | ||
| 744 | ret = -EIO; | ||
| 745 | if (!valid_signal(data)) | ||
| 746 | break; | ||
| 747 | if (request == PTRACE_SYSCALL) | ||
| 748 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
| 749 | else | ||
| 750 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
| 751 | child->exit_code = data; | ||
| 752 | single_step_disable(child); | ||
| 753 | wake_up_process(child); | ||
| 754 | ret = 0; | ||
| 755 | break; | ||
| 756 | |||
| 757 | /* | ||
| 758 | * make the child exit. Best I can do is send it a sigkill. | ||
| 759 | * perhaps it should be put in the status that it wants to | ||
| 760 | * exit. | ||
| 761 | */ | ||
| 762 | case PTRACE_KILL: | ||
| 763 | single_step_disable(child); | ||
| 764 | if (child->exit_state != EXIT_ZOMBIE) { | ||
| 765 | child->exit_code = SIGKILL; | ||
| 766 | wake_up_process(child); | ||
| 767 | } | ||
| 768 | ret = 0; | ||
| 769 | break; | ||
| 770 | |||
| 771 | /* | ||
| 772 | * execute single instruction. | ||
| 773 | */ | ||
| 774 | case PTRACE_SINGLESTEP: | ||
| 775 | ret = -EIO; | ||
| 776 | if (!valid_signal(data)) | ||
| 777 | break; | ||
| 778 | single_step_enable(child); | ||
| 779 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
| 780 | child->exit_code = data; | ||
| 781 | /* give it a chance to run. */ | ||
| 782 | wake_up_process(child); | ||
| 783 | ret = 0; | ||
| 784 | break; | ||
| 785 | |||
| 786 | case PTRACE_GETREGS: | 767 | case PTRACE_GETREGS: |
| 787 | ret = ptrace_getregs(child, (void __user *)data); | 768 | ret = ptrace_getregs(child, (void __user *)data); |
| 788 | break; | 769 | break; |
