diff options
Diffstat (limited to 'arch/powerpc/kernel/ptrace.c')
-rw-r--r-- | arch/powerpc/kernel/ptrace.c | 325 |
1 files changed, 218 insertions, 107 deletions
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index bf76562167c3..0fb53950da43 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -35,11 +35,11 @@ #include <asm/pgtable.h> #include <asm/system.h> -#ifdef CONFIG_PPC64 -#include "ptrace-common.h" -#endif +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ -#ifdef CONFIG_PPC32 /* * Set of msr bits that gdb can change on behalf of a process. */ @@ -48,65 +48,117 @@ #else #define MSR_DEBUGCHANGE (MSR_SE | MSR_BE) #endif -#endif /* CONFIG_PPC32 */ /* - * does not yet catch signals sent when the child dies. - * in exit.c or in signal.c. + * Max register writeable via put_reg */ - #ifdef CONFIG_PPC32 +#define PT_MAX_PUT_REG PT_MQ +#else +#define PT_MAX_PUT_REG PT_CCR +#endif + /* * Get contents of register REGNO in task TASK. */ -static inline unsigned long get_reg(struct task_struct *task, int regno) +unsigned long ptrace_get_reg(struct task_struct *task, int regno) { - if (regno < sizeof(struct pt_regs) / sizeof(unsigned long) - && task->thread.regs != NULL) + unsigned long tmp = 0; + + if (task->thread.regs == NULL) + return -EIO; + + if (regno == PT_MSR) { + tmp = ((unsigned long *)task->thread.regs)[PT_MSR]; + return tmp | task->thread.fpexc_mode; + } + + if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) return ((unsigned long *)task->thread.regs)[regno]; - return (0); + + return -EIO; } /* * Write contents of register REGNO in task TASK. */ -static inline int put_reg(struct task_struct *task, int regno, - unsigned long data) +int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) { - if (regno <= PT_MQ && task->thread.regs != NULL) { + if (task->thread.regs == NULL) + return -EIO; + + if (regno <= PT_MAX_PUT_REG || regno == PT_TRAP) { if (regno == PT_MSR) data = (data & MSR_DEBUGCHANGE) | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + /* We prevent mucking around with the reserved area of trap + * which are used internally by the kernel + */ + if (regno == PT_TRAP) + data &= 0xfff0; ((unsigned long *)task->thread.regs)[regno] = data; return 0; } return -EIO; } + +static int get_fpregs(void __user *data, struct task_struct *task, + int has_fpscr) +{ + unsigned int count = has_fpscr ? 33 : 32; + + if (copy_to_user(data, task->thread.fpr, count * sizeof(double))) + return -EFAULT; + return 0; +} + +static int set_fpregs(void __user *data, struct task_struct *task, + int has_fpscr) +{ + unsigned int count = has_fpscr ? 33 : 32; + + if (copy_from_user(task->thread.fpr, data, count * sizeof(double))) + return -EFAULT; + return 0; +} + + #ifdef CONFIG_ALTIVEC /* + * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. + * The transfer totals 34 quadword. Quadwords 0-31 contain the + * corresponding vector registers. Quadword 32 contains the vscr as the + * last word (offset 12) within that quadword. Quadword 33 contains the + * vrsave as the first word (offset 0) within the quadword. + * + * This definition of the VMX state is compatible with the current PPC32 + * ptrace interface. This allows signal handling and ptrace to use the + * same structures. This also simplifies the implementation of a bi-arch + * (combined (32- and 64-bit) gdb. + */ + +/* * Get contents of AltiVec register state in task TASK */ -static inline int get_vrregs(unsigned long __user *data, struct task_struct *task) +static int get_vrregs(unsigned long __user *data, struct task_struct *task) { - int i, j; - - if (!access_ok(VERIFY_WRITE, data, 133 * sizeof(unsigned long))) - return -EFAULT; + unsigned long regsize; /* copy AltiVec registers VR[0] .. VR[31] */ - for (i = 0; i < 32; i++) - for (j = 0; j < 4; j++, data++) - if (__put_user(task->thread.vr[i].u[j], data)) - return -EFAULT; + regsize = 32 * sizeof(vector128); + if (copy_to_user(data, task->thread.vr, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); /* copy VSCR */ - for (i = 0; i < 4; i++, data++) - if (__put_user(task->thread.vscr.u[i], data)) - return -EFAULT; + regsize = 1 * sizeof(vector128); + if (copy_to_user(data, &task->thread.vscr, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); - /* copy VRSAVE */ - if (__put_user(task->thread.vrsave, data)) + /* copy VRSAVE */ + if (put_user(task->thread.vrsave, (u32 __user *)data)) return -EFAULT; return 0; @@ -115,31 +167,29 @@ static inline int get_vrregs(unsigned long __user *data, struct task_struct *tas /* * Write contents of AltiVec register state into task TASK. */ -static inline int set_vrregs(struct task_struct *task, unsigned long __user *data) +static int set_vrregs(struct task_struct *task, unsigned long __user *data) { - int i, j; - - if (!access_ok(VERIFY_READ, data, 133 * sizeof(unsigned long))) - return -EFAULT; + unsigned long regsize; /* copy AltiVec registers VR[0] .. VR[31] */ - for (i = 0; i < 32; i++) - for (j = 0; j < 4; j++, data++) - if (__get_user(task->thread.vr[i].u[j], data)) - return -EFAULT; + regsize = 32 * sizeof(vector128); + if (copy_from_user(task->thread.vr, data, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); /* copy VSCR */ - for (i = 0; i < 4; i++, data++) - if (__get_user(task->thread.vscr.u[i], data)) - return -EFAULT; + regsize = 1 * sizeof(vector128); + if (copy_from_user(&task->thread.vscr, data, regsize)) + return -EFAULT; + data += (regsize / sizeof(unsigned long)); /* copy VRSAVE */ - if (__get_user(task->thread.vrsave, data)) + if (get_user(task->thread.vrsave, (u32 __user *)data)) return -EFAULT; return 0; } -#endif +#endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_SPE @@ -156,7 +206,7 @@ static inline int set_vrregs(struct task_struct *task, unsigned long __user *dat /* * Get contents of SPE register state in task TASK. */ -static inline int get_evrregs(unsigned long *data, struct task_struct *task) +static int get_evrregs(unsigned long *data, struct task_struct *task) { int i; @@ -182,7 +232,7 @@ static inline int get_evrregs(unsigned long *data, struct task_struct *task) /* * Write contents of SPE register state into task TASK. */ -static inline int set_evrregs(struct task_struct *task, unsigned long *data) +static int set_evrregs(struct task_struct *task, unsigned long *data) { int i; @@ -205,8 +255,8 @@ static inline int set_evrregs(struct task_struct *task, unsigned long *data) } #endif /* CONFIG_SPE */ -static inline void -set_single_step(struct task_struct *task) + +static void set_single_step(struct task_struct *task) { struct pt_regs *regs = task->thread.regs; @@ -221,8 +271,7 @@ set_single_step(struct task_struct *task) set_tsk_thread_flag(task, TIF_SINGLESTEP); } -static inline void -clear_single_step(struct task_struct *task) +static void clear_single_step(struct task_struct *task) { struct pt_regs *regs = task->thread.regs; @@ -236,7 +285,25 @@ clear_single_step(struct task_struct *task) } clear_tsk_thread_flag(task, TIF_SINGLESTEP); } -#endif /* CONFIG_PPC32 */ + +static int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, + unsigned long data) +{ + /* We only support one DABR and no IABRS at the moment */ + if (addr > 0) + return -EINVAL; + + /* The bottom 3 bits are flags */ + if ((data & ~0x7UL) >= TASK_SIZE) + return -EIO; + + /* Ensure translation is on */ + if (data && !(data & DABR_TRANSLATION)) + return -EIO; + + task->thread.dabr = data; + return 0; +} /* * Called by kernel/ptrace.c when detaching.. @@ -249,6 +316,62 @@ void ptrace_disable(struct task_struct *child) clear_single_step(child); } +/* + * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, + * we mark them as obsolete now, they will be removed in a future version + */ +static long arch_ptrace_old(struct task_struct *child, long request, long addr, + long data) +{ + int ret = -EPERM; + + switch(request) { + case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned long __user *tmp = (unsigned long __user *)addr; + + for (i = 0; i < 32; i++) { + ret = put_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ + int i; + unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; + unsigned long __user *tmp = (unsigned long __user *)addr; + + for (i = 0; i < 32; i++) { + ret = get_user(*reg, tmp); + if (ret) + break; + reg++; + tmp++; + } + break; + } + + case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ + flush_fp_to_thread(child); + ret = get_fpregs((void __user *)addr, child, 0); + break; + } + + case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ + flush_fp_to_thread(child); + ret = set_fpregs((void __user *)addr, child, 0); + break; + } + + } + return ret; +} + long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret = -EPERM; @@ -284,11 +407,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #endif break; -#ifdef CONFIG_PPC32 CHECK_FULL_REGS(child->thread.regs); -#endif if (index < PT_FPR0) { - tmp = get_reg(child, (int) index); + tmp = ptrace_get_reg(child, (int) index); } else { flush_fp_to_thread(child); tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0]; @@ -323,13 +444,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #endif break; -#ifdef CONFIG_PPC32 CHECK_FULL_REGS(child->thread.regs); -#endif - if (index == PT_ORIG_R3) - break; if (index < PT_FPR0) { - ret = put_reg(child, index, data); + ret = ptrace_put_reg(child, index, data); } else { flush_fp_to_thread(child); ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data; @@ -384,7 +501,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } -#ifdef CONFIG_PPC64 case PTRACE_GET_DEBUGREG: { ret = -EINVAL; /* We only support one DABR and no IABRS at the moment */ @@ -398,73 +514,61 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) case PTRACE_SET_DEBUGREG: ret = ptrace_set_debugreg(child, addr, data); break; -#endif case PTRACE_DETACH: ret = ptrace_detach(child, data); break; - case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; +#ifdef CONFIG_PPC64 + case PTRACE_GETREGS64: +#endif + case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ + int ui; + if (!access_ok(VERIFY_WRITE, (void __user *)data, + sizeof(struct pt_regs))) { + ret = -EIO; + break; + } + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret |= __put_user(ptrace_get_reg(child, ui), + (unsigned long __user *) data); + data += sizeof(long); } break; } - case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); +#ifdef CONFIG_PPC64 + case PTRACE_SETREGS64: +#endif + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + int ui; + if (!access_ok(VERIFY_READ, (void __user *)data, + sizeof(struct pt_regs))) { + ret = -EIO; + break; + } + ret = 0; + for (ui = 0; ui < PT_REGS_COUNT; ui ++) { + ret = __get_user(tmp, (unsigned long __user *) data); if (ret) break; - reg++; - tmp++; + ptrace_put_reg(child, ui, tmp); + data += sizeof(long); } break; } - case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - + case PTRACE_GETFPREGS: { /* Get the child FPU state (FPR0...31 + FPSCR) */ flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } + ret = get_fpregs((void __user *)data, child, 1); break; } - case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.fpr)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - + case PTRACE_SETFPREGS: { /* Set the child FPU state (FPR0...31 + FPSCR) */ flush_fp_to_thread(child); - - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } + ret = set_fpregs((void __user *)data, child, 1); break; } @@ -499,11 +603,18 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; #endif + /* Old reverse args ptrace callss */ + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ + case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */ + ret = arch_ptrace_old(child, request, addr, data); + break; + default: ret = ptrace_request(child, request, addr, data); break; } - return ret; } |