X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Farm%2Fkernel%2Fptrace.c;h=b145c7e1fca858018e5220ac997b1649ea304ce6;hb=refs%2Fheads%2Fvserver;hp=4bcf7f6a43ee1d9d3d7bf661de989d3d2f266f4b;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 4bcf7f6a4..b145c7e1f 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -52,23 +53,6 @@ #define BREAKINST_THUMB 0xde01 #endif -/* - * Get the address of the live pt_regs for the specified task. - * These are saved onto the top kernel stack when the process - * is not running. - * - * Note: if a user thread is execve'd from kernel space, the - * kernel stack will not be empty on entry to the kernel, so - * ptracing these tasks will fail. - */ -static inline struct pt_regs * -get_user_regs(struct task_struct *task) -{ - return (struct pt_regs *) - ((unsigned long)task->thread_info + THREAD_SIZE - - 8 - sizeof(struct pt_regs)); -} - /* * this routine will get a word off of the processes privileged stack. * the offset is how far from the base addr as stored in the THREAD. @@ -77,7 +61,7 @@ get_user_regs(struct task_struct *task) */ static inline long get_user_reg(struct task_struct *task, int offset) { - return get_user_regs(task)->uregs[offset]; + return task_pt_regs(task)->uregs[offset]; } /* @@ -89,7 +73,7 @@ static inline long get_user_reg(struct task_struct *task, int offset) static inline int put_user_reg(struct task_struct *task, int offset, long data) { - struct pt_regs newregs, *regs = get_user_regs(task); + struct pt_regs newregs, *regs = task_pt_regs(task); int ret = -EINVAL; newregs = *regs; @@ -240,6 +224,15 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in */ long aluop1, aluop2, ccbit; + if ((insn & 0x0fffffd0) == 0x012fff10) { + /* + * bx or blx + */ + alt = get_user_reg(child, insn & 15); + break; + } + + if ((insn & 0xf000) != 0xf000) break; @@ -410,7 +403,7 @@ void ptrace_set_bpt(struct task_struct *child) u32 insn; int res; - regs = get_user_regs(child); + regs = task_pt_regs(child); pc = instruction_pointer(regs); if (thumb_mode(regs)) { @@ -485,7 +478,7 @@ void ptrace_break(struct task_struct *tsk, struct pt_regs *regs) info.si_signo = SIGTRAP; info.si_errno = 0; info.si_code = TRAP_BRKPT; - info.si_addr = (void *)instruction_pointer(regs); + info.si_addr = (void __user *)instruction_pointer(regs); force_sig_info(SIGTRAP, &info, tsk); } @@ -561,7 +554,7 @@ static int ptrace_write_user(struct task_struct *tsk, unsigned long off, */ static int ptrace_getregs(struct task_struct *tsk, void __user *uregs) { - struct pt_regs *regs = get_user_regs(tsk); + struct pt_regs *regs = task_pt_regs(tsk); return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0; } @@ -576,7 +569,7 @@ static int ptrace_setregs(struct task_struct *tsk, void __user *uregs) ret = -EFAULT; if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) { - struct pt_regs *regs = get_user_regs(tsk); + struct pt_regs *regs = task_pt_regs(tsk); ret = -EINVAL; if (valid_user_regs(&newregs)) { @@ -593,7 +586,7 @@ static int ptrace_setregs(struct task_struct *tsk, void __user *uregs) */ static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp) { - return copy_to_user(ufp, &tsk->thread_info->fpstate, + return copy_to_user(ufp, &task_thread_info(tsk)->fpstate, sizeof(struct user_fp)) ? -EFAULT : 0; } @@ -602,13 +595,71 @@ static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp) */ static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp) { - struct thread_info *thread = tsk->thread_info; + struct thread_info *thread = task_thread_info(tsk); thread->used_cp[1] = thread->used_cp[2] = 1; return copy_from_user(&thread->fpstate, ufp, sizeof(struct user_fp)) ? -EFAULT : 0; } -static int do_ptrace(int request, struct task_struct *child, long addr, long data) +#ifdef CONFIG_IWMMXT + +/* + * Get the child iWMMXt state. + */ +static int ptrace_getwmmxregs(struct task_struct *tsk, void __user *ufp) +{ + struct thread_info *thread = task_thread_info(tsk); + + if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT)) + return -ENODATA; + iwmmxt_task_disable(thread); /* force it to ram */ + return copy_to_user(ufp, &thread->fpstate.iwmmxt, IWMMXT_SIZE) + ? -EFAULT : 0; +} + +/* + * Set the child iWMMXt state. + */ +static int ptrace_setwmmxregs(struct task_struct *tsk, void __user *ufp) +{ + struct thread_info *thread = task_thread_info(tsk); + + if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT)) + return -EACCES; + iwmmxt_task_release(thread); /* force a reload */ + return copy_from_user(&thread->fpstate.iwmmxt, ufp, IWMMXT_SIZE) + ? -EFAULT : 0; +} + +#endif + +#ifdef CONFIG_CRUNCH +/* + * Get the child Crunch state. + */ +static int ptrace_getcrunchregs(struct task_struct *tsk, void __user *ufp) +{ + struct thread_info *thread = task_thread_info(tsk); + + crunch_task_disable(thread); /* force it to ram */ + return copy_to_user(ufp, &thread->crunchstate, CRUNCH_SIZE) + ? -EFAULT : 0; +} + +/* + * Set the child Crunch state. + */ +static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp) +{ + struct thread_info *thread = task_thread_info(tsk); + + crunch_task_release(thread); /* force a reload */ + return copy_from_user(&thread->crunchstate, ufp, CRUNCH_SIZE) + ? -EFAULT : 0; +} +#endif + +long arch_ptrace(struct task_struct *child, long request, long addr, long data) { unsigned long tmp; int ret; @@ -654,7 +705,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat case PTRACE_SYSCALL: case PTRACE_CONT: ret = -EIO; - if ((unsigned long) data > _NSIG) + if (!valid_signal(data)) break; if (request == PTRACE_SYSCALL) set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); @@ -677,7 +728,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat /* make sure single-step breakpoint is gone. */ child->ptrace &= ~PT_SINGLESTEP; ptrace_cancel_bpt(child); - if (child->state != TASK_ZOMBIE) { + if (child->exit_state != EXIT_ZOMBIE) { child->exit_code = SIGKILL; wake_up_process(child); } @@ -689,7 +740,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat */ case PTRACE_SINGLESTEP: ret = -EIO; - if ((unsigned long) data > _NSIG) + if (!valid_signal(data)) break; child->ptrace |= PT_SINGLESTEP; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); @@ -719,94 +770,60 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat ret = ptrace_setfpregs(child, (void __user *)data); break; - default: - ret = ptrace_request(child, request, addr, data); +#ifdef CONFIG_IWMMXT + case PTRACE_GETWMMXREGS: + ret = ptrace_getwmmxregs(child, (void __user *)data); break; - } - return ret; -} + case PTRACE_SETWMMXREGS: + ret = ptrace_setwmmxregs(child, (void __user *)data); + break; +#endif -asmlinkage int sys_ptrace(long request, long pid, long addr, long data) -{ - struct task_struct *child; - int ret; + case PTRACE_GET_THREAD_AREA: + ret = put_user(task_thread_info(child)->tp_value, + (unsigned long __user *) data); + break; - lock_kernel(); - ret = -EPERM; - if (request == PTRACE_TRACEME) { - /* are we already being traced? */ - if (current->ptrace & PT_PTRACED) - goto out; - ret = security_ptrace(current->parent, current); - if (ret) - goto out; - /* set the ptrace bit in the process flags. */ - current->ptrace |= PT_PTRACED; - ret = 0; - goto out; - } - ret = -ESRCH; - read_lock(&tasklist_lock); - child = find_task_by_pid(pid); - if (child) - get_task_struct(child); - read_unlock(&tasklist_lock); - if (!child) - goto out; - if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT)) - goto out_tsk; - - ret = -EPERM; - if (pid == 1) /* you may not mess with init */ - goto out_tsk; - - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - goto out_tsk; + case PTRACE_SET_SYSCALL: + ret = 0; + child->ptrace_message = data; + break; + +#ifdef CONFIG_CRUNCH + case PTRACE_GETCRUNCHREGS: + ret = ptrace_getcrunchregs(child, (void __user *)data); + break; + + case PTRACE_SETCRUNCHREGS: + ret = ptrace_setcrunchregs(child, (void __user *)data); + break; +#endif + + default: + ret = ptrace_request(child, request, addr, data); + break; } - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret == 0) - ret = do_ptrace(request, child, addr, data); - -out_tsk: - put_task_struct(child); -out: - unlock_kernel(); + return ret; } -asmlinkage void syscall_trace(int why, struct pt_regs *regs) +asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) { unsigned long ip; - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; - if (!(current->ptrace & PT_PTRACED)) - return; + if (test_thread_flag(TIF_SYSCALL_TRACE)) { + /* + * Save IP. IP is used to denote syscall entry/exit: + * IP = 0 -> entry, = 1 -> exit + */ + ip = regs->ARM_ip; + regs->ARM_ip = why; - /* - * Save IP. IP is used to denote syscall entry/exit: - * IP = 0 -> entry, = 1 -> exit - */ - ip = regs->ARM_ip; - regs->ARM_ip = why; - - /* the 0x80 provides a way for the tracing parent to distinguish - between a syscall stop and SIGTRAP delivery */ - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; + tracehook_report_syscall(regs, why); + + regs->ARM_ip = ip; } - regs->ARM_ip = ip; + + return scno; }