aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/ptrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/kernel/ptrace.c')
-rw-r--r--arch/arm/kernel/ptrace.c113
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
455void user_disable_single_step(struct task_struct *task)
456{
457 task->ptrace &= ~PT_SINGLESTEP;
458 ptrace_cancel_bpt(task);
459}
460
461void 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 */
458void ptrace_disable(struct task_struct *child) 469void 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
513static 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
535static 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
502static int __init ptrace_break_init(void) 543static 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;