diff -X exclude -urN linux-2.6.20-mm2.orig/arch/sh/kernel/Makefile linux-2.6.20-mm2/arch/sh/kernel/Makefile --- linux-2.6.20-mm2.orig/arch/sh/kernel/Makefile 2007-02-19 16:42:51.000000000 +0900 +++ linux-2.6.20-mm2/arch/sh/kernel/Makefile 2007-02-19 18:25:59.000000000 +0900 @@ -2,6 +2,8 @@ # Makefile for the Linux/SuperH kernel. # +CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' + extra-y := head.o init_task.o vmlinux.lds obj-y := process.o signal.o traps.o irq.o \ diff -X exclude -urN linux-2.6.20-mm2.orig/arch/sh/kernel/entry-common.S linux-2.6.20-mm2/arch/sh/kernel/entry-common.S --- linux-2.6.20-mm2.orig/arch/sh/kernel/entry-common.S 2007-02-19 16:42:51.000000000 +0900 +++ linux-2.6.20-mm2/arch/sh/kernel/entry-common.S 2007-02-19 18:36:51.000000000 +0900 @@ -233,8 +233,8 @@ nop #endif sti - ! XXX setup arguments... - mov.l 4f, r0 ! do_syscall_trace + mov.l 8f, r0 ! syscall_trace_leave + mov r15, r4 jsr @r0 nop bra resume_userspace @@ -243,8 +243,8 @@ .align 2 syscall_trace_entry: ! Yes it is traced. - ! XXX setup arguments... - mov.l 4f, r11 ! Call do_syscall_trace which notifies + mov.l 7f, r11 ! Call syscall_trace_enter which notifies + mov r15, r4 jsr @r11 ! superior (will chomp R[0-7]) nop ! Reload R0-R4 from kernel stack, where the @@ -404,8 +404,9 @@ #endif 2: .long NR_syscalls 3: .long sys_call_table -4: .long do_syscall_trace #ifdef CONFIG_TRACE_IRQFLAGS 5: .long trace_hardirqs_on 6: .long trace_hardirqs_off #endif +7: .long syscall_trace_enter +8: .long syscall_trace_leave diff -X exclude -urN linux-2.6.20-mm2.orig/arch/sh/kernel/process.c linux-2.6.20-mm2/arch/sh/kernel/process.c --- linux-2.6.20-mm2.orig/arch/sh/kernel/process.c 2007-02-19 16:42:51.000000000 +0900 +++ linux-2.6.20-mm2/arch/sh/kernel/process.c 2007-02-19 16:47:05.000000000 +0900 @@ -438,11 +438,6 @@ (char __user * __user *)uargv, (char __user * __user *)uenvp, regs); - if (error == 0) { - task_lock(current); - current->ptrace &= ~PT_DTRACE; - task_unlock(current); - } putname(filename); out: return error; diff -X exclude -urN linux-2.6.20-mm2.orig/arch/sh/kernel/ptrace.c linux-2.6.20-mm2/arch/sh/kernel/ptrace.c --- linux-2.6.20-mm2.orig/arch/sh/kernel/ptrace.c 2007-02-19 16:42:51.000000000 +0900 +++ linux-2.6.20-mm2/arch/sh/kernel/ptrace.c 2007-02-19 18:53:19.000000000 +0900 @@ -6,25 +6,17 @@ * edited by Linus Torvalds * * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka - * + * Copyright (C) 2007 Paul Mundt */ #include -#include -#include -#include -#include #include #include -#include -#include #include -#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include /* * does not yet catch signals sent when the child dies. @@ -57,7 +49,19 @@ return 0; } -static void ptrace_disable_singlestep(struct task_struct *child) +void tracehook_enable_single_step(struct task_struct *child) +{ + /* Next scheduling will set up UBC */ + if (child->thread.ubc_pc) + ubc_usercnt += 1; + + child->thread.ubc_pc = + get_stack_long(child, offsetof(struct pt_regs, pc)); + + set_tsk_thread_flag(child, TIF_SINGLESTEP); +} + +void tracehook_disable_single_step(struct task_struct *child) { clear_tsk_thread_flag(child, TIF_SINGLESTEP); @@ -81,210 +85,84 @@ */ void ptrace_disable(struct task_struct *child) { - ptrace_disable_singlestep(child); + tracehook_disable_single_step(child); } -long arch_ptrace(struct task_struct *child, long request, long addr, long data) +static int genregs_get(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - struct user * dummy = NULL; - int ret; - - switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long *) data); - break; - } - - /* read the word at location addr in the USER area. */ - case PTRACE_PEEKUSR: { - unsigned long tmp; - - ret = -EIO; - if ((addr & 3) || addr < 0 || - addr > sizeof(struct user) - 3) - break; - - if (addr < sizeof(struct pt_regs)) - tmp = get_stack_long(child, addr); - else if (addr >= (long) &dummy->fpu && - addr < (long) &dummy->u_fpvalid) { - if (!tsk_used_math(child)) { - if (addr == (long)&dummy->fpu.fpscr) - tmp = FPSCR_INIT; - else - tmp = 0; - } else - tmp = ((long *)&child->thread.fpu) - [(addr - (long)&dummy->fpu) >> 2]; - } else if (addr == (long) &dummy->u_fpvalid) - tmp = !!tsk_used_math(child); - else - tmp = 0; - ret = put_user(tmp, (unsigned long *)data); - break; - } + return utrace_regset_copyout(&pos, &count, &kbuf, &ubuf, + task_pt_regs(target), 0, -1); +} - /* when I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; - break; - - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - ret = -EIO; - if ((addr & 3) || addr < 0 || - addr > sizeof(struct user) - 3) - break; - - if (addr < sizeof(struct pt_regs)) - ret = put_stack_long(child, addr, data); - else if (addr >= (long) &dummy->fpu && - addr < (long) &dummy->u_fpvalid) { - set_stopped_child_used_math(child); - ((long *)&child->thread.fpu) - [(addr - (long)&dummy->fpu) >> 2] = data; - ret = 0; - } else if (addr == (long) &dummy->u_fpvalid) { - conditional_stopped_child_used_math(data, child); - ret = 0; - } - break; - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: { /* restart after signal. */ - ret = -EIO; - if (!valid_signal(data)) - break; - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - - ptrace_disable_singlestep(child); - - child->exit_code = data; - wake_up_process(child); - ret = 0; - break; - } +static int genregs_set(struct task_struct *target, + const struct utrace_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + return utrace_regset_copyin(&pos, &count, &kbuf, &ubuf, + task_pt_regs(target), 0, -1); +} /* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. + * These are our native regset flavours. */ - case PTRACE_KILL: { - ret = 0; - if (child->exit_state == EXIT_ZOMBIE) /* already dead */ - break; - ptrace_disable_singlestep(child); - child->exit_code = SIGKILL; - wake_up_process(child); - break; +static const struct utrace_regset native_regsets[] = { + { + .n = ELF_NGREG, + .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), + .get = genregs_get, .set = genregs_set, + }, +}; + +const struct utrace_regset_view utrace_sh_native_view = { + .name = UTS_MACHINE, .e_machine = ELF_ARCH, + .regsets = native_regsets, + .n = ARRAY_SIZE(native_regsets), +}; +EXPORT_SYMBOL_GPL(utrace_sh_native_view); + +#ifdef CONFIG_PTRACE +static const struct ptrace_layout_segment sh_uarea[] = { + { 0, ELF_NGREG * sizeof(elf_greg_t), 0, 0 }, + { 0, 0, -1, 0 }, +}; + +int arch_ptrace(long *req, struct task_struct *child, + struct utrace_attached_engine *engine, + unsigned long addr, unsigned long data, long *val) +{ + switch (*req) { + case PTRACE_PEEKUSR: + return ptrace_peekusr(child, engine, sh_uarea, addr, data); + case PTRACE_POKEUSR: + return ptrace_pokeusr(child, engine, sh_uarea, addr, data); + case PTRACE_GETREGS: + return ptrace_whole_regset(child, engine, data, 0, 0); + case PTRACE_SETREGS: + return ptrace_whole_regset(child, engine, data, 0, 1); } - case PTRACE_SINGLESTEP: { /* set the trap flag. */ - long pc; - struct pt_regs *dummy = NULL; - - ret = -EIO; - if (!valid_signal(data)) - break; - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - if ((child->ptrace & PT_DTRACE) == 0) { - /* Spurious delayed TF traps may occur */ - child->ptrace |= PT_DTRACE; - } - - pc = get_stack_long(child, (long)&dummy->pc); - - /* Next scheduling will set up UBC */ - if (child->thread.ubc_pc == 0) - ubc_usercnt += 1; - child->thread.ubc_pc = pc; - - set_tsk_thread_flag(child, TIF_SINGLESTEP); - child->exit_code = data; - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; - break; - } - - case PTRACE_DETACH: /* detach a process that was attached. */ - ret = ptrace_detach(child, data); - break; - -#ifdef CONFIG_SH_DSP - case PTRACE_GETDSPREGS: { - unsigned long dp; - - ret = -EIO; - dp = ((unsigned long) child) + THREAD_SIZE - - sizeof(struct pt_dspregs); - if (*((int *) (dp - 4)) == SR_FD) { - copy_to_user(addr, (void *) dp, - sizeof(struct pt_dspregs)); - ret = 0; - } - break; - } - - case PTRACE_SETDSPREGS: { - unsigned long dp; - - ret = -EIO; - dp = ((unsigned long) child) + THREAD_SIZE - - sizeof(struct pt_dspregs); - if (*((int *) (dp - 4)) == SR_FD) { - copy_from_user((void *) dp, addr, - sizeof(struct pt_dspregs)); - ret = 0; - } - break; - } + return -ENOSYS; +} #endif - default: - ret = ptrace_request(child, request, addr, data); - break; - } - return ret; +asmlinkage void syscall_trace_enter(struct pt_regs __regs) +{ + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall(regs, 0); } -asmlinkage void do_syscall_trace(void) +asmlinkage void syscall_trace_leave(struct pt_regs __regs) { - struct task_struct *tsk = current; + struct pt_regs *regs = RELOC_HIDE(&__regs, 0); - if (!test_thread_flag(TIF_SYSCALL_TRACE) && - !test_thread_flag(TIF_SINGLESTEP)) - return; - if (!(tsk->ptrace & PT_PTRACED)) - return; - /* 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) && - !test_thread_flag(TIF_SINGLESTEP) ? 0x80 : 0)); - - /* - * 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 (tsk->exit_code) { - send_sig(tsk->exit_code, tsk, 1); - tsk->exit_code = 0; + if (test_thread_flag(TIF_SINGLESTEP)) { + force_sig(SIGTRAP, current); /* XXX */ + tracehook_report_syscall_step(regs); } } diff -X exclude -urN linux-2.6.20-mm2.orig/arch/sh/kernel/signal.c linux-2.6.20-mm2/arch/sh/kernel/signal.c --- linux-2.6.20-mm2.orig/arch/sh/kernel/signal.c 2007-02-19 16:42:51.000000000 +0900 +++ linux-2.6.20-mm2/arch/sh/kernel/signal.c 2007-02-19 16:47:05.000000000 +0900 @@ -24,7 +24,7 @@ #include #include #include - +#include #include #include #include @@ -540,6 +540,8 @@ sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); + + tracehook_report_handle_signal(sig, ka, oldset, regs); } return ret; @@ -598,7 +600,7 @@ if (regs->regs[0] == -ERESTARTNOHAND || regs->regs[0] == -ERESTARTSYS || regs->regs[0] == -ERESTARTNOINTR) { - regs->regs[0] = save_r0; + regs->regs[0] = save_r0; regs->pc -= 2; } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { regs->pc -= 2; diff -X exclude -urN linux-2.6.20-mm2.orig/include/asm-sh/ptrace.h linux-2.6.20-mm2/include/asm-sh/ptrace.h --- linux-2.6.20-mm2.orig/include/asm-sh/ptrace.h 2007-02-05 03:44:54.000000000 +0900 +++ linux-2.6.20-mm2/include/asm-sh/ptrace.h 2007-02-19 18:29:16.000000000 +0900 @@ -28,7 +28,7 @@ #define REG_PR 17 #define REG_SR 18 -#define REG_GBR 19 +#define REG_GBR 19 #define REG_MACH 20 #define REG_MACL 21 @@ -81,6 +81,10 @@ unsigned long mod; }; +#define PTRACE_GETREGS 12 +#define PTRACE_SETREGS 13 +#define PTRACE_GETFPREGS 14 +#define PTRACE_SETFPREGS 15 #define PTRACE_GETDSPREGS 55 #define PTRACE_SETDSPREGS 56 diff -X exclude -urN linux-2.6.20-mm2.orig/include/asm-sh/tracehook.h linux-2.6.20-mm2/include/asm-sh/tracehook.h --- linux-2.6.20-mm2.orig/include/asm-sh/tracehook.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.20-mm2/include/asm-sh/tracehook.h 2007-02-19 18:11:00.000000000 +0900 @@ -0,0 +1,48 @@ +/* + * Tracing hooks, SuperH CPU support + * + * Copyright (C) 2007 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive for + * more details. + */ +#ifndef __ASM_SH_TRACEHOOK_H +#define __ASM_SH_TRACEHOOK_H + +#include +#include + +/* + * See linux/tracehook.h for the descriptions of what these need to do. + */ + +#define ARCH_HAS_SINGLE_STEP (1) + +static inline void tracehook_enable_syscall_trace(struct task_struct *tsk) +{ + set_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE); +} + +static inline void tracehook_disable_syscall_trace(struct task_struct *tsk) +{ + clear_tsk_thread_flag(tsk, TIF_SYSCALL_TRACE); +} + +static inline void tracehook_abort_syscall(struct pt_regs *regs) +{ + regs->regs[3] = -1L; +} + +extern const struct utrace_regset_view utrace_sh_native_view; +static inline const struct utrace_regset_view * +utrace_native_view(struct task_struct *tsk) +{ + return &utrace_sh_native_view; +} + +/* arch/sh/kernel/ptrace.c */ +void tracehook_enable_single_step(struct task_struct *tsk); +void tracehook_disable_single_step(struct task_struct *tsk); + +#endif /* __ASM_SH_TRACEHOOK_H */