diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index bb28087..64072ea 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o +obj-$(CONFIG_UTRACE) += utrace.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index b145c7e..0eb9478 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include "ptrace.h" +#include "utrace.h" #define REG_PC 15 #define REG_PSR 16 @@ -456,14 +458,22 @@ void ptrace_cancel_bpt(struct task_struct *child) } /* + * make sure single-step breakpoint is gone. + */ +static inline void ptrace_disable_singlestep(struct task_struct *task) +{ +#if 0 + task->ptrace &= ~PT_SINGLESTEP; + ptrace_cancel_bpt(task); +#endif +} + +/* * Called by kernel/ptrace.c when detaching.. - * - * Make sure the single step bit is not set. */ void ptrace_disable(struct task_struct *child) { - child->ptrace &= ~PT_SINGLESTEP; - ptrace_cancel_bpt(child); + ptrace_disable_singlestep(child); } /* @@ -514,191 +524,73 @@ static int __init ptrace_break_init(void) core_initcall(ptrace_break_init); -/* - * Read the word at offset "off" into the "struct user". We - * actually access the pt_regs stored on the kernel stack. - */ -static int ptrace_read_user(struct task_struct *tsk, unsigned long off, - unsigned long __user *ret) -{ - unsigned long tmp; - - if (off & 3 || off >= sizeof(struct user)) - return -EIO; - - tmp = 0; - if (off < sizeof(struct pt_regs)) - tmp = get_user_reg(tsk, off >> 2); - - return put_user(tmp, ret); -} - -/* - * Write the word at offset "off" into "struct user". We - * actually access the pt_regs stored on the kernel stack. - */ -static int ptrace_write_user(struct task_struct *tsk, unsigned long off, - unsigned long val) -{ - if (off & 3 || off >= sizeof(struct user)) - return -EIO; - - if (off >= sizeof(struct pt_regs)) - return 0; - - return put_user_reg(tsk, off >> 2, val); -} - -/* - * Get all user integer registers. - */ -static int ptrace_getregs(struct task_struct *tsk, void __user *uregs) -{ - struct pt_regs *regs = task_pt_regs(tsk); - - return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0; -} +static const struct ptrace_layout_segment arm_uarea[] = { + { + .start = 0, + .end = sizeof(struct pt_regs), + .regset = REGSET_ARM, + .offset = 0, + }, + { + .start = 0, + .end = 0, + .regset = -1, + .offset = 0, + } +}; -/* - * Set all user integer registers. - */ -static int ptrace_setregs(struct task_struct *tsk, void __user *uregs) +int arch_ptrace(long *req, struct task_struct *child, + struct utrace_attached_engine *engine, + unsigned long addr, unsigned long data, long *val) { - struct pt_regs newregs; + long request = *req; int ret; - ret = -EFAULT; - if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) { - struct pt_regs *regs = task_pt_regs(tsk); - - ret = -EINVAL; - if (valid_user_regs(&newregs)) { - *regs = newregs; - ret = 0; - } - } - - return ret; -} + switch (request) { + case PTRACE_PEEKUSR: + ret = ptrace_peekusr(child, engine, arm_uarea, addr, data); + break; + case PTRACE_POKEUSR: + ret = ptrace_pokeusr(child, engine, arm_uarea, addr, data); + break; -/* - * Get the child FPU state. - */ -static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp) -{ - return copy_to_user(ufp, &task_thread_info(tsk)->fpstate, - sizeof(struct user_fp)) ? -EFAULT : 0; -} + case PTRACE_GETREGS: + ret = ptrace_whole_regset(child, engine, data, REGSET_ARM, 0); + break; + case PTRACE_SETREGS: + ret = ptrace_whole_regset(child, engine, data, REGSET_ARM, 1); + break; -/* - * Set the child FPU state. - */ -static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp) -{ - 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; -} + case PTRACE_GETFPREGS: + ret = ptrace_whole_regset(child, engine, data, REGSET_FPA, 0); + break; + case PTRACE_SETFPREGS: + ret = ptrace_whole_regset(child, engine, data, REGSET_FPA, 1); + break; #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; -} - + case PTRACE_GETWMMXREGS: + ret = ptrace_whole_regset(child, engine, data, REGSET_IWMMXT, 0); + break; + case PTRACE_SETWMMXREGS: + ret = ptrace_whole_regset(child, engine, data, REGSET_IWMMXT, 1); + break; #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; -} + case PTRACE_GETCRUNCHREGS: + ret = ptrace_whole_regset(child, engine, data, REGSET_CRUNCH, 0); + break; + case PTRACE_SETCRUNCHREGS: + ret = ptrace_whole_regset(child, engine, data, REGSET_CRUNCH, 1); + break; #endif -long arch_ptrace(struct task_struct *child, long request, long addr, long data) -{ - unsigned long tmp; - int ret; - - switch (request) { - /* - * read word at location "addr" in the child process. - */ - case PTRACE_PEEKTEXT: - case PTRACE_PEEKDATA: - ret = access_process_vm(child, addr, &tmp, - sizeof(unsigned long), 0); - if (ret == sizeof(unsigned long)) - ret = put_user(tmp, (unsigned long __user *) data); - else - ret = -EIO; - break; - - case PTRACE_PEEKUSR: - ret = ptrace_read_user(child, addr, (unsigned long __user *)data); - break; - - /* - * write the word at location addr. - */ - case PTRACE_POKETEXT: - case PTRACE_POKEDATA: - ret = access_process_vm(child, addr, &data, - sizeof(unsigned long), 1); - if (ret == sizeof(unsigned long)) - ret = 0; - else - ret = -EIO; - break; - - case PTRACE_POKEUSR: - ret = ptrace_write_user(child, addr, data); - break; + case PTRACE_GET_THREAD_AREA: + ret = put_user(task_thread_info(child)->tp_value, + (unsigned long __user *) data); + break; +#if 0 /* * continue/restart and stop at next (return from) syscall */ @@ -712,9 +604,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) else clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; - /* make sure single-step breakpoint is gone. */ - child->ptrace &= ~PT_SINGLESTEP; - ptrace_cancel_bpt(child); + ptrace_disable_singlestep(child); wake_up_process(child); ret = 0; break; @@ -725,9 +615,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) * exit. */ case PTRACE_KILL: - /* make sure single-step breakpoint is gone. */ - child->ptrace &= ~PT_SINGLESTEP; - ptrace_cancel_bpt(child); + ptrace_disable_singlestep(child); if (child->exit_state != EXIT_ZOMBIE) { child->exit_code = SIGKILL; wake_up_process(child); @@ -750,59 +638,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ret = 0; break; - case PTRACE_DETACH: - ret = ptrace_detach(child, data); - break; - - case PTRACE_GETREGS: - ret = ptrace_getregs(child, (void __user *)data); - break; - - case PTRACE_SETREGS: - ret = ptrace_setregs(child, (void __user *)data); - break; - - case PTRACE_GETFPREGS: - ret = ptrace_getfpregs(child, (void __user *)data); - break; - - case PTRACE_SETFPREGS: - ret = ptrace_setfpregs(child, (void __user *)data); - break; - -#ifdef CONFIG_IWMMXT - case PTRACE_GETWMMXREGS: - ret = ptrace_getwmmxregs(child, (void __user *)data); - break; - - case PTRACE_SETWMMXREGS: - ret = ptrace_setwmmxregs(child, (void __user *)data); - break; -#endif - - case PTRACE_GET_THREAD_AREA: - ret = put_user(task_thread_info(child)->tp_value, - (unsigned long __user *) data); - break; - - 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; + /* FIXME: Sorry, utrace can't support this */ + case PTRACE_SET_SYSCALL: + ret = 0; + child->ptrace_message = data; + break; #endif - default: - ret = ptrace_request(child, request, addr, data); - break; + default: + ret = -ENOSYS; + break; } return ret; @@ -820,7 +665,13 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) ip = regs->ARM_ip; regs->ARM_ip = why; + /* + * FIXME: Save scno so arch_ptrace can change the system call number + */ tracehook_report_syscall(regs, why); + /* + * FIXME: Set scno from something to indicate the new syscall number + */ regs->ARM_ip = ip; } diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 3843d3b..cd5c584 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -9,7 +9,9 @@ */ #include #include +#include #include +#include #include #include @@ -262,6 +264,33 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf) return err; } +static inline void single_step_trap(struct task_struct *task) +{ +#if 0 + /* Send SIGTRAP if we're single-stepping */ + if (task->ptrace & PT_SINGLESTEP) { + ptrace_cancel_bpt(task); + send_sig(SIGTRAP, task, 1); + } +#endif +} + +static inline void single_step_clear(struct task_struct *task) +{ +#if 0 + if (task->ptrace & PT_SINGLESTEP) + ptrace_cancel_bpt(task); +#endif +} + +static inline void single_step_set(struct task_struct *task) +{ +#if 0 + if (task->ptrace & PT_SINGLESTEP) + ptrace_set_bpt(task); +#endif +} + asmlinkage int sys_sigreturn(struct pt_regs *regs) { struct sigframe __user *frame; @@ -285,11 +314,7 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs) if (restore_sigframe(regs, frame)) goto badframe; - /* Send SIGTRAP if we're single-stepping */ - if (current->ptrace & PT_SINGLESTEP) { - ptrace_cancel_bpt(current); - send_sig(SIGTRAP, current, 1); - } + single_step_trap(current); return regs->ARM_r0; @@ -324,11 +349,7 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) if (do_sigaltstack(&frame->sig.uc.uc_stack, NULL, regs->ARM_sp) == -EFAULT) goto badframe; - /* Send SIGTRAP if we're single-stepping */ - if (current->ptrace & PT_SINGLESTEP) { - ptrace_cancel_bpt(current); - send_sig(SIGTRAP, current, 1); - } + single_step_trap(current); return regs->ARM_r0; @@ -615,6 +636,8 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, recalc_sigpending(); spin_unlock_irq(&tsk->sighand->siglock); + tracehook_report_handle_signal(sig, ka, oldset, regs); + single_step_set(tsk); } /* @@ -644,14 +667,11 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) if (try_to_freeze()) goto no_signal; - if (current->ptrace & PT_SINGLESTEP) - ptrace_cancel_bpt(current); + single_step_clear(current); signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { handle_signal(signr, &ka, &info, oldset, regs, syscall); - if (current->ptrace & PT_SINGLESTEP) - ptrace_set_bpt(current); return 1; } @@ -705,8 +725,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) restart_syscall(regs); } } - if (current->ptrace & PT_SINGLESTEP) - ptrace_set_bpt(current); + single_step_set(current); return 0; } --- /dev/null 2007-03-03 11:02:56.785804854 +0000 +++ b/arch/arm/kernel/utrace.c 2007-03-03 17:21:14.000000000 +0000 @@ -0,0 +1,151 @@ +#include +#include +#include + +#include "utrace.h" + +static int +getregs_arm(struct task_struct *task, const struct utrace_regset *set, + unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) +{ + void *regs = task_pt_regs(task)->uregs; + return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, 0, -1); +} + +static int +setregs_arm(struct task_struct *task, const struct utrace_regset *set, + unsigned int pos, unsigned int count, const void *kbuf, + const void __user *ubuf) +{ + struct pt_regs newregs, *regs = task_pt_regs(task); + int ret; + + newregs = *regs; + ret = utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, newregs.uregs, + 0, -1); + if (ret >= 0) { + if (valid_user_regs(&newregs)) + *regs = newregs; + else + ret = -EINVAL; + } + return ret; +} + +static int +getregs_fpa(struct task_struct *task, const struct utrace_regset *set, + unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) +{ + struct thread_info *thread = task_thread_info(task); + return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, + &thread->fpstate, 0, -1); +} + +static int +setregs_fpa(struct task_struct *task, const struct utrace_regset *set, + unsigned int pos, unsigned int count, const void *kbuf, + const void __user *ubuf) +{ + struct thread_info *thread = task_thread_info(task); + thread->used_cp[1] = thread->used_cp[2] = 1; + return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, + &thread->fpstate, 0, -1); +} + +#ifdef CONFIG_IWMMXT +static int +getregs_iwmmxt(struct task_struct *task, const struct utrace_regset *set, + unsigned int pos, unsigned int count, void *kbuf, + void __user *ubuf) +{ + struct thread_info *thread = task_thread_info(task); + + if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT)) + return 0; + + iwmmxt_task_disable(thread); /* force it to ram */ + return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, + &thread->fpstate.iwmmxt, 0, -1); +} + +static int +setregs_iwmmxt(struct task_struct *task, const struct utrace_regset *set, + unsigned int pos, unsigned int count, const void *kbuf, + const void __user *ubuf) +{ + struct thread_info *thread = task_thread_info(task); + + if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT)) + return 0; + + iwmmxt_task_release(thread); /* force a reload */ + return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, + &thread->crunchstate, 0, -1); +} +#endif + +#ifdef CONFIG_CRUNCH +static int +getregs_fpa(struct task_struct *task, const struct utrace_regset *set, + unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) +{ + struct thread_info *thread = task_thread_info(task); + crunch_task_disable(thread); + return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, + &thread->crunchstate, 0, -1); +} + +static int +setregs_fpa(struct task_struct *task, const struct utrace_regset *set, + unsigned int pos, unsigned int count, const void *kbuf, + const void __user *ubuf) +{ + struct thread_info *thread = task_thread_info(task); + crunch_task_release(thread); + return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, + &thread->crunchstate, 0, -1); +} +#endif + +static const struct utrace_regset native_regsets[] = { + [REGSET_ARM] = { + .n = sizeof(struct pt_regs) / sizeof(u32), + .size = sizeof(u32), + .align = sizeof(u32), + .get = getregs_arm, + .set = setregs_arm, + }, + [REGSET_FPA] = { + .n = sizeof(struct user_fp), + .size = sizeof(u8), + .align = sizeof(u32), + .get = getregs_fpa, + .set = setregs_fpa, + }, +#ifdef CONFIG_IWMMXT + [REGSET_IWMMX] = { + .n = IWMMXT_SIZE, + .size = sizeof(u8), + .align = sizeof(u32), + .get = getregs_iwmmxt, + .set = setregs_iwmmxt, + }, +#endif +#ifdef CONFIG_CRUNCH + [REGSET_CRUNCH] = { + .n = CRUNCH_SIZE, + .size = sizeof(u8), + .align = sizeof(u32), + .get = getregs_crunch, + .set = setregs_crunch, + }, +#endif +}; + +const struct utrace_regset_view utrace_arm_native = { + .name = "arm", + .e_machine = EM_ARM, + .regsets = native_regsets, + .n = ARRAY_SIZE(native_regsets), +}; +EXPORT_SYMBOL_GPL(utrace_arm_native); --- /dev/null 2007-03-03 11:02:56.785804854 +0000 +++ b/arch/arm/kernel/utrace.h 2007-03-03 16:42:49.000000000 +0000 @@ -0,0 +1,4 @@ +#define REGSET_ARM 0 +#define REGSET_FPA 1 +#define REGSET_IWMMX 2 +#define REGSET_CRUNCH 3 --- /dev/null 2007-03-03 11:02:56.785804854 +0000 +++ b/include/asm-arm/tracehook.h 2007-03-03 17:41:21.000000000 +0000 @@ -0,0 +1,26 @@ +#ifndef ASMARM_TRACEHOOK_H +#define ASMARM_TRACEHOOK_H + +static inline void tracehook_enable_syscall_trace(struct task_struct *task) +{ + set_tsk_thread_flag(task, TIF_SYSCALL_TRACE); +} + +static inline void tracehook_disable_syscall_trace(struct task_struct *task) +{ + clear_tsk_thread_flag(task, TIF_SYSCALL_TRACE); +} + +static inline void tracehook_abort_syscall(struct pt_regs *regs) +{ + /* FIXME: Sorry, ARM can't support this */ +} + +static inline const +struct utrace_regset_view *utrace_native_view(struct task_struct *task) +{ + extern const struct utrace_regset_view utrace_arm_native; + return &utrace_arm_native; +} + +#endif