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; |