Signed-off-by: Mike Frysinger <vapier@xxxxxxxxxx> --- arch/blackfin/Kconfig | 1 + arch/blackfin/include/asm/ptrace.h | 23 ++++++++ arch/blackfin/include/asm/syscall.h | 96 +++++++++++++++++++++++++++++++++++ arch/blackfin/kernel/ptrace.c | 71 +++++++------------------- arch/blackfin/kernel/signal.c | 4 +- arch/blackfin/mach-common/entry.S | 6 ++- 6 files changed, 145 insertions(+), 56 deletions(-) create mode 100644 arch/blackfin/include/asm/syscall.h diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index cdd87a0..13d8fe9 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -24,6 +24,7 @@ config RWSEM_XCHGADD_ALGORITHM config BLACKFIN def_bool y select HAVE_ARCH_KGDB + select HAVE_ARCH_TRACEHOOK select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_IDE diff --git a/arch/blackfin/include/asm/ptrace.h b/arch/blackfin/include/asm/ptrace.h index b33a448..f3e75fa 100644 --- a/arch/blackfin/include/asm/ptrace.h +++ b/arch/blackfin/include/asm/ptrace.h @@ -24,6 +24,8 @@ #ifndef __ASSEMBLY__ +struct task_struct; + /* this struct defines the way the registers are stored on the stack during a system call. */ @@ -101,9 +103,30 @@ struct pt_regs { master interrupt enable. */ #define user_mode(regs) (!(((regs)->ipend & ~0x10) & (((regs)->ipend & ~0x10) - 1))) #define instruction_pointer(regs) ((regs)->pc) +#define user_stack_pointer(regs) ((regs)->usp) #define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); +#define arch_has_single_step() (1) +extern void user_enable_single_step(struct task_struct *); +extern void user_disable_single_step(struct task_struct *child); +/* common code demands this function */ +#define ptrace_disable(child) user_disable_single_step(child) + +/* + * 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. + */ +#define task_pt_regs(task) \ + (struct pt_regs *) \ + ((unsigned long)task_stack_page(task) + \ + (THREAD_SIZE - sizeof(struct pt_regs))) + #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ diff --git a/arch/blackfin/include/asm/syscall.h b/arch/blackfin/include/asm/syscall.h new file mode 100644 index 0000000..74e0458 --- /dev/null +++ b/arch/blackfin/include/asm/syscall.h @@ -0,0 +1,96 @@ +/* + * Magic syscall break down functions + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __ASM_BLACKFIN_SYSCALL_H__ +#define __ASM_BLACKFIN_SYSCALL_H__ + +/* + * Blackfin syscalls are simple: + * enter: + * p0: syscall number + * r{0,1,2,3,4,5}: syscall args 0,1,2,3,4,5 + * exit: + * r0: return/error value + */ + +#include <linux/err.h> +#include <linux/sched.h> +#include <asm/ptrace.h> + +static inline long +syscall_get_nr(struct task_struct *task, struct pt_regs *regs) +{ + return regs->p0; +} + +static inline void +syscall_rollback(struct task_struct *task, struct pt_regs *regs) +{ + /* was zu tun !? */ +} + +static inline long +syscall_get_error(struct task_struct *task, struct pt_regs *regs) +{ + return IS_ERR_VALUE(regs->r0) ? regs->r0 : 0; +} + +static inline long +syscall_get_return_value(struct task_struct *task, struct pt_regs *regs) +{ + return regs->r0; +} + +static inline void +syscall_set_return_value(struct task_struct *task, struct pt_regs *regs, + int error, long val) +{ + regs->r0 = error ? -error : val; +} + +static inline void +syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, + unsigned int i, unsigned int n, unsigned long *args) +{ + /* wtf is "i" ? */ + BUG_ON(i); + + switch (n) { + case 6: args[5] = regs->r5; + case 5: args[4] = regs->r4; + case 4: args[3] = regs->r3; + case 3: args[2] = regs->r2; + case 2: args[1] = regs->r1; + case 1: args[0] = regs->r0; + break; + default: + BUG(); + } +} + +static inline void +syscall_set_arguments(struct task_struct *task, struct pt_regs *regs, + unsigned int i, unsigned int n, const unsigned long *args) +{ + /* wtf is "i" ? */ + BUG_ON(i); + + switch (n) { + case 6: regs->r5 = args[5]; + case 5: regs->r4 = args[4]; + case 4: regs->r3 = args[3]; + case 3: regs->r2 = args[2]; + case 2: regs->r1 = args[1]; + case 1: regs->r0 = args[0]; + break; + default: + BUG(); + } +} + +#endif diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c index 65567dc..f6e94ad 100644 --- a/arch/blackfin/kernel/ptrace.c +++ b/arch/blackfin/kernel/ptrace.c @@ -13,6 +13,7 @@ #include <linux/ptrace.h> #include <linux/user.h> #include <linux/signal.h> +#include <linux/tracehook.h> #include <linux/uaccess.h> #include <asm/page.h> @@ -37,32 +38,13 @@ /* sets the trace bits. */ #define TRACE_BITS 0x0001 -/* Find the stack offset for a register, relative to thread.esp0. */ -#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) - -/* - * 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_stack_page(task) + - (THREAD_SIZE - sizeof(struct pt_regs))); -} - /* * Get all user integer registers. */ static inline int ptrace_getregs(struct task_struct *tsk, void __user *uregs) { struct pt_regs regs; - memcpy(®s, get_user_regs(tsk), sizeof(regs)); + memcpy(®s, task_pt_regs(tsk), sizeof(regs)); regs.usp = tsk->thread.usp; return copy_to_user(uregs, ®s, sizeof(struct pt_regs)) ? -EFAULT : 0; } @@ -78,10 +60,8 @@ static inline int ptrace_getregs(struct task_struct *tsk, void __user *uregs) static inline long get_reg(struct task_struct *task, int regno) { unsigned char *reg_ptr; + struct pt_regs *regs = task_pt_regs(task); - struct pt_regs *regs = - (struct pt_regs *)((unsigned long)task_stack_page(task) + - (THREAD_SIZE - sizeof(struct pt_regs))); reg_ptr = (char *)regs; switch (regno) { @@ -104,10 +84,8 @@ static inline int put_reg(struct task_struct *task, int regno, unsigned long data) { char *reg_ptr; + struct pt_regs *regs = task_pt_regs(task); - struct pt_regs *regs = - (struct pt_regs *)((unsigned long)task_stack_page(task) + - (THREAD_SIZE - sizeof(struct pt_regs))); reg_ptr = (char *)regs; switch (regno) { @@ -160,7 +138,7 @@ static inline int is_user_addr_valid(struct task_struct *child, return -EIO; } -void ptrace_enable(struct task_struct *child) +void user_enable_single_step(struct task_struct *child) { unsigned long tmp; tmp = get_reg(child, PT_SYSCFG) | (TRACE_BITS); @@ -169,10 +147,8 @@ void ptrace_enable(struct task_struct *child) /* * Called by kernel/ptrace.c when detaching.. - * - * Make sure the single step bit is not set. */ -void ptrace_disable(struct task_struct *child) +void user_disable_single_step(struct task_struct *child) { unsigned long tmp; /* make sure the single step bit is not set. */ @@ -366,7 +342,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; - ptrace_disable(child); + user_disable_single_step(child); pr_debug("ptrace: before wake_up_process\n"); wake_up_process(child); ret = 0; @@ -382,7 +358,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; - ptrace_disable(child); + user_disable_single_step(child); wake_up_process(child); break; @@ -392,7 +368,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) if (!valid_signal(data)) break; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - ptrace_enable(child); + user_enable_single_step(child); child->exit_code = data; wake_up_process(child); ret = 0; @@ -417,27 +393,18 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) return ret; } -asmlinkage void syscall_trace(void) +asmlinkage int syscall_trace_enter(struct pt_regs *regs) { - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; + int ret = 0; - if (!(current->ptrace & PT_PTRACED)) - return; + if (test_thread_flag(TIF_SYSCALL_TRACE)) + ret = tracehook_report_syscall_entry(regs); - /* the 0x80 provides a way for the tracing parent to distinguish - * between a syscall stop and SIGTRAP delivery - */ - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0)); + return ret; +} - /* - * 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; - } +asmlinkage void syscall_trace_leave(struct pt_regs *regs) +{ + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall_exit(regs, 0); } diff --git a/arch/blackfin/kernel/signal.c b/arch/blackfin/kernel/signal.c index e0fd63e..1f28852 100644 --- a/arch/blackfin/kernel/signal.c +++ b/arch/blackfin/kernel/signal.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2009 Analog Devices Inc. + * Copyright 2004-2010 Analog Devices Inc. * * Licensed under the GPL-2 or later */ @@ -213,7 +213,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t * info, */ if (regs->syscfg & TRACE_BITS) { regs->syscfg &= ~TRACE_BITS; - ptrace_notify(SIGTRAP); + tracehook_signal_handler(sig, info, ka, regs, 1); } return 0; diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S index 16abeb8..365d506 100644 --- a/arch/blackfin/mach-common/entry.S +++ b/arch/blackfin/mach-common/entry.S @@ -736,7 +736,8 @@ ENDPROC(_system_call) * this symbol need not be global anyways, so ... */ _sys_trace: - pseudo_long_call _syscall_trace, p5; + r0 = sp; + pseudo_long_call _syscall_trace_enter, p5; /* Execute the appropriate system call */ @@ -760,7 +761,8 @@ _sys_trace: SP += 24; [sp + PT_R0] = r0; - pseudo_long_call _syscall_trace, p5; + r0 = sp; + pseudo_long_call _syscall_trace_leave, p5; jump .Lresume_userspace; ENDPROC(_sys_trace) -- 1.6.6.1 -- To unsubscribe from this list: send the line "unsubscribe linux-arch" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html