[PATCH 4a] utrace: ia64 ptrace compatibility This patch implements ptrace compatibility for ia64. Signed-off-by: Roland McGrath Signed-off-by: Anil S Keshavamurthy Signed-off-by: Bibo mao --- arch/ia64/ia32/sys_ia32.c | 40 + arch/ia64/kernel/ptrace.c | 1024 +++++----------------------------------------- 2 files changed, 159 insertions(+), 905 deletions(-) Index: b/arch/ia64/ia32/sys_ia32.c =================================================================== --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -2327,6 +2327,46 @@ const struct utrace_regset_view utrace_i }; #endif +#ifdef CONFIG_PTRACE +/* + * This matches the arch/i386/kernel/ptrace.c definitions. + */ + +static const struct ptrace_layout_segment ia32_uarea[] = { + {0, sizeof(struct user_regs_struct32), 0, 0}, + {0, 0, -1, 0} +}; + +fastcall int arch_compat_ptrace(compat_long_t *request, + struct task_struct *child, + struct utrace_attached_engine *engine, + compat_ulong_t addr, compat_ulong_t data, + compat_long_t *retval) +{ + switch (*request) { + case PTRACE_PEEKUSR: + return ptrace_compat_peekusr(child, engine, ia32_uarea, + addr, data); + case PTRACE_POKEUSR: + return ptrace_compat_pokeusr(child, engine, ia32_uarea, + addr, data); + case IA32_PTRACE_GETREGS: + return ptrace_whole_regset(child, engine, data, 0, 0); + case IA32_PTRACE_SETREGS: + return ptrace_whole_regset(child, engine, data, 0, 1); + case IA32_PTRACE_GETFPREGS: + return ptrace_whole_regset(child, engine, data, 1, 0); + case IA32_PTRACE_SETFPREGS: + return ptrace_whole_regset(child, engine, data, 1, 1); + case IA32_PTRACE_GETFPXREGS: + return ptrace_whole_regset(child, engine, data, 2, 0); + case IA32_PTRACE_SETFPXREGS: + return ptrace_whole_regset(child, engine, data, 2, 1); + } + return -ENOSYS; +} +#endif + typedef struct { unsigned int ss_sp; unsigned int ss_flags; Index: b/arch/ia64/kernel/ptrace.c =================================================================== --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -626,81 +626,6 @@ void ia64_sync_krbs(void) unw_init_running(do_sync_rbs, ia64_sync_kernel_rbs); } -#if 0 /* XXX */ -static inline int -thread_matches (struct task_struct *thread, unsigned long addr) -{ - unsigned long thread_rbs_end; - struct pt_regs *thread_regs; - - if (ptrace_check_attach(thread, 0) < 0) - /* - * If the thread is not in an attachable state, we'll - * ignore it. The net effect is that if ADDR happens - * to overlap with the portion of the thread's - * register backing store that is currently residing - * on the thread's kernel stack, then ptrace() may end - * up accessing a stale value. But if the thread - * isn't stopped, that's a problem anyhow, so we're - * doing as well as we can... - */ - return 0; - - thread_regs = task_pt_regs(thread); - thread_rbs_end = ia64_get_user_rbs_end(thread, thread_regs, NULL); - if (!on_kernel_rbs(addr, thread_regs->ar_bspstore, thread_rbs_end)) - return 0; - - return 1; /* looks like we've got a winner */ -} - -/* - * GDB apparently wants to be able to read the register-backing store - * of any thread when attached to a given process. If we are peeking - * or poking an address that happens to reside in the kernel-backing - * store of another thread, we need to attach to that thread, because - * otherwise we end up accessing stale data. - * - * task_list_lock must be read-locked before calling this routine! - */ -static struct task_struct * -find_thread_for_addr (struct task_struct *child, unsigned long addr) -{ - struct task_struct *p; - struct mm_struct *mm; - struct list_head *this, *next; - int mm_users; - - if (!(mm = get_task_mm(child))) - return child; - - /* -1 because of our get_task_mm(): */ - mm_users = atomic_read(&mm->mm_users) - 1; - if (mm_users <= 1) - goto out; /* not multi-threaded */ - - /* - * Traverse the current process' children list. Every task that - * one attaches to becomes a child. And it is only attached children - * of the debugger that are of interest (ptrace_check_attach checks - * for this). - */ - list_for_each_safe(this, next, ¤t->children) { - p = list_entry(this, struct task_struct, sibling); - if (p->mm != mm) - continue; - if (thread_matches(p, addr)) { - child = p; - goto out; - } - } - - out: - mmput(mm); - return child; -} -#endif - /* * Write f32-f127 back to task->thread.fph if it has been modified. */ @@ -864,836 +789,6 @@ access_nat_bits (struct task_struct *chi return 0; } -#if 0 -static int -access_uarea (struct task_struct *child, unsigned long addr, - unsigned long *data, int write_access) -{ - unsigned long *ptr, regnum, urbs_end, rnat_addr, cfm; - struct switch_stack *sw; - struct pt_regs *pt; -# define pt_reg_addr(pt, reg) ((void *) \ - ((unsigned long) (pt) \ - + offsetof(struct pt_regs, reg))) - - - pt = task_pt_regs(child); - sw = (struct switch_stack *) (child->thread.ksp + 16); - - if ((addr & 0x7) != 0) { - dprintk("ptrace: unaligned register address 0x%lx\n", addr); - return -1; - } - - if (addr < PT_F127 + 16) { - /* accessing fph */ - if (write_access) - ia64_sync_fph(child); - else - ia64_flush_fph(child); - ptr = (unsigned long *) - ((unsigned long) &child->thread.fph + addr); - } else if ((addr >= PT_F10) && (addr < PT_F11 + 16)) { - /* scratch registers untouched by kernel (saved in pt_regs) */ - ptr = pt_reg_addr(pt, f10) + (addr - PT_F10); - } else if (addr >= PT_F12 && addr < PT_F15 + 16) { - /* - * Scratch registers untouched by kernel (saved in - * switch_stack). - */ - ptr = (unsigned long *) ((long) sw - + (addr - PT_NAT_BITS - 32)); - } else if (addr < PT_AR_LC + 8) { - /* preserved state: */ - struct unw_frame_info info; - char nat = 0; - int ret; - - unw_init_from_blocked_task(&info, child); - if (unw_unwind_to_user(&info) < 0) - return -1; - - switch (addr) { - case PT_NAT_BITS: - return access_nat_bits(child, pt, &info, - data, write_access); - - case PT_R4: case PT_R5: case PT_R6: case PT_R7: - if (write_access) { - /* read NaT bit first: */ - unsigned long dummy; - - ret = unw_get_gr(&info, (addr - PT_R4)/8 + 4, - &dummy, &nat); - if (ret < 0) - return ret; - } - return unw_access_gr(&info, (addr - PT_R4)/8 + 4, data, - &nat, write_access); - - case PT_B1: case PT_B2: case PT_B3: - case PT_B4: case PT_B5: - return unw_access_br(&info, (addr - PT_B1)/8 + 1, data, - write_access); - - case PT_AR_EC: - return unw_access_ar(&info, UNW_AR_EC, data, - write_access); - - case PT_AR_LC: - return unw_access_ar(&info, UNW_AR_LC, data, - write_access); - - default: - if (addr >= PT_F2 && addr < PT_F5 + 16) - return access_fr(&info, (addr - PT_F2)/16 + 2, - (addr & 8) != 0, data, - write_access); - else if (addr >= PT_F16 && addr < PT_F31 + 16) - return access_fr(&info, - (addr - PT_F16)/16 + 16, - (addr & 8) != 0, - data, write_access); - else { - dprintk("ptrace: rejecting access to register " - "address 0x%lx\n", addr); - return -1; - } - } - } else if (addr < PT_F9+16) { - /* scratch state */ - switch (addr) { - case PT_AR_BSP: - /* - * By convention, we use PT_AR_BSP to refer to - * the end of the user-level backing store. - * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof) - * to get the real value of ar.bsp at the time - * the kernel was entered. - * - * Furthermore, when changing the contents of - * PT_AR_BSP (or PT_CFM) we MUST copy any - * users-level stacked registers that are - * stored on the kernel stack back to - * user-space because otherwise, we might end - * up clobbering kernel stacked registers. - * Also, if this happens while the task is - * blocked in a system call, which convert the - * state such that the non-system-call exit - * path is used. This ensures that the proper - * state will be picked up when resuming - * execution. However, it *also* means that - * once we write PT_AR_BSP/PT_CFM, it won't be - * possible to modify the syscall arguments of - * the pending system call any longer. This - * shouldn't be an issue because modifying - * PT_AR_BSP/PT_CFM generally implies that - * we're either abandoning the pending system - * call or that we defer it's re-execution - * (e.g., due to GDB doing an inferior - * function call). - */ - urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); - if (write_access) { - if (*data != urbs_end) { - if (ia64_sync_user_rbs(child, sw, - pt->ar_bspstore, - urbs_end) < 0) - return -1; - if (in_syscall(pt)) - convert_to_non_syscall(child, - pt, - cfm); - /* - * Simulate user-level write - * of ar.bsp: - */ - pt->loadrs = 0; - pt->ar_bspstore = *data; - } - } else - *data = urbs_end; - return 0; - - case PT_CFM: - urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); - if (write_access) { - if (((cfm ^ *data) & PFM_MASK) != 0) { - if (ia64_sync_user_rbs(child, sw, - pt->ar_bspstore, - urbs_end) < 0) - return -1; - if (in_syscall(pt)) - convert_to_non_syscall(child, - pt, - cfm); - pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK) - | (*data & PFM_MASK)); - } - } else - *data = cfm; - return 0; - - case PT_CR_IPSR: - if (write_access) { - unsigned long tmp = *data; - /* psr.ri==3 is a reserved value: SDM 2:25 */ - if ((tmp & IA64_PSR_RI) == IA64_PSR_RI) - tmp &= ~IA64_PSR_RI; - pt->cr_ipsr = ((tmp & IPSR_MASK) - | (pt->cr_ipsr & ~IPSR_MASK)); - } else - *data = (pt->cr_ipsr & IPSR_MASK); - return 0; - - case PT_AR_RSC: - if (write_access) - pt->ar_rsc = *data | (3 << 2); /* force PL3 */ - else - *data = pt->ar_rsc; - return 0; - - case PT_AR_RNAT: - urbs_end = ia64_get_user_rbs_end(child, pt, NULL); - rnat_addr = (long) ia64_rse_rnat_addr((long *) - urbs_end); - if (write_access) - return ia64_poke(child, sw, urbs_end, - rnat_addr, *data); - else - return ia64_peek(child, sw, urbs_end, - rnat_addr, data); - - case PT_R1: - ptr = pt_reg_addr(pt, r1); - break; - case PT_R2: case PT_R3: - ptr = pt_reg_addr(pt, r2) + (addr - PT_R2); - break; - case PT_R8: case PT_R9: case PT_R10: case PT_R11: - ptr = pt_reg_addr(pt, r8) + (addr - PT_R8); - break; - case PT_R12: case PT_R13: - ptr = pt_reg_addr(pt, r12) + (addr - PT_R12); - break; - case PT_R14: - ptr = pt_reg_addr(pt, r14); - break; - case PT_R15: - ptr = pt_reg_addr(pt, r15); - break; - case PT_R16: case PT_R17: case PT_R18: case PT_R19: - case PT_R20: case PT_R21: case PT_R22: case PT_R23: - case PT_R24: case PT_R25: case PT_R26: case PT_R27: - case PT_R28: case PT_R29: case PT_R30: case PT_R31: - ptr = pt_reg_addr(pt, r16) + (addr - PT_R16); - break; - case PT_B0: - ptr = pt_reg_addr(pt, b0); - break; - case PT_B6: - ptr = pt_reg_addr(pt, b6); - break; - case PT_B7: - ptr = pt_reg_addr(pt, b7); - break; - case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8: - case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8: - ptr = pt_reg_addr(pt, f6) + (addr - PT_F6); - break; - case PT_AR_BSPSTORE: - ptr = pt_reg_addr(pt, ar_bspstore); - break; - case PT_AR_UNAT: - ptr = pt_reg_addr(pt, ar_unat); - break; - case PT_AR_PFS: - ptr = pt_reg_addr(pt, ar_pfs); - break; - case PT_AR_CCV: - ptr = pt_reg_addr(pt, ar_ccv); - break; - case PT_AR_FPSR: - ptr = pt_reg_addr(pt, ar_fpsr); - break; - case PT_CR_IIP: - ptr = pt_reg_addr(pt, cr_iip); - break; - case PT_PR: - ptr = pt_reg_addr(pt, pr); - break; - /* scratch register */ - - default: - /* disallow accessing anything else... */ - dprintk("ptrace: rejecting access to register " - "address 0x%lx\n", addr); - return -1; - } - } else if (addr <= PT_AR_SSD) { - ptr = pt_reg_addr(pt, ar_csd) + (addr - PT_AR_CSD); - } else { - /* access debug registers */ - - if (addr >= PT_IBR) { - regnum = (addr - PT_IBR) >> 3; - ptr = &child->thread.ibr[0]; - } else { - regnum = (addr - PT_DBR) >> 3; - ptr = &child->thread.dbr[0]; - } - - if (regnum >= 8) { - dprintk("ptrace: rejecting access to register " - "address 0x%lx\n", addr); - return -1; - } -#ifdef CONFIG_PERFMON - /* - * Check if debug registers are used by perfmon. This - * test must be done once we know that we can do the - * operation, i.e. the arguments are all valid, but - * before we start modifying the state. - * - * Perfmon needs to keep a count of how many processes - * are trying to modify the debug registers for system - * wide monitoring sessions. - * - * We also include read access here, because they may - * cause the PMU-installed debug register state - * (dbr[], ibr[]) to be reset. The two arrays are also - * used by perfmon, but we do not use - * IA64_THREAD_DBG_VALID. The registers are restored - * by the PMU context switch code. - */ - if (pfm_use_debug_registers(child)) return -1; -#endif - - if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { - child->thread.flags |= IA64_THREAD_DBG_VALID; - memset(child->thread.dbr, 0, - sizeof(child->thread.dbr)); - memset(child->thread.ibr, 0, - sizeof(child->thread.ibr)); - } - - ptr += regnum; - - if ((regnum & 1) && write_access) { - /* don't let the user set kernel-level breakpoints: */ - *ptr = *data & ~(7UL << 56); - return 0; - } - } - if (write_access) - *ptr = *data; - else - *data = *ptr; - return 0; -} - -static long -ptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr) -{ - unsigned long psr, ec, lc, rnat, bsp, cfm, nat_bits, val; - struct unw_frame_info info; - struct ia64_fpreg fpval; - struct switch_stack *sw; - struct pt_regs *pt; - long ret, retval = 0; - char nat = 0; - int i; - - if (!access_ok(VERIFY_WRITE, ppr, sizeof(struct pt_all_user_regs))) - return -EIO; - - pt = task_pt_regs(child); - sw = (struct switch_stack *) (child->thread.ksp + 16); - unw_init_from_blocked_task(&info, child); - if (unw_unwind_to_user(&info) < 0) { - return -EIO; - } - - if (((unsigned long) ppr & 0x7) != 0) { - dprintk("ptrace:unaligned register address %p\n", ppr); - return -EIO; - } - - if (access_uarea(child, PT_CR_IPSR, &psr, 0) < 0 - || access_uarea(child, PT_AR_EC, &ec, 0) < 0 - || access_uarea(child, PT_AR_LC, &lc, 0) < 0 - || access_uarea(child, PT_AR_RNAT, &rnat, 0) < 0 - || access_uarea(child, PT_AR_BSP, &bsp, 0) < 0 - || access_uarea(child, PT_CFM, &cfm, 0) - || access_uarea(child, PT_NAT_BITS, &nat_bits, 0)) - return -EIO; - - /* control regs */ - - retval |= __put_user(pt->cr_iip, &ppr->cr_iip); - retval |= __put_user(psr, &ppr->cr_ipsr); - - /* app regs */ - - retval |= __put_user(pt->ar_pfs, &ppr->ar[PT_AUR_PFS]); - retval |= __put_user(pt->ar_rsc, &ppr->ar[PT_AUR_RSC]); - retval |= __put_user(pt->ar_bspstore, &ppr->ar[PT_AUR_BSPSTORE]); - retval |= __put_user(pt->ar_unat, &ppr->ar[PT_AUR_UNAT]); - retval |= __put_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]); - retval |= __put_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]); - - retval |= __put_user(ec, &ppr->ar[PT_AUR_EC]); - retval |= __put_user(lc, &ppr->ar[PT_AUR_LC]); - retval |= __put_user(rnat, &ppr->ar[PT_AUR_RNAT]); - retval |= __put_user(bsp, &ppr->ar[PT_AUR_BSP]); - retval |= __put_user(cfm, &ppr->cfm); - - /* gr1-gr3 */ - - retval |= __copy_to_user(&ppr->gr[1], &pt->r1, sizeof(long)); - retval |= __copy_to_user(&ppr->gr[2], &pt->r2, sizeof(long) *2); - - /* gr4-gr7 */ - - for (i = 4; i < 8; i++) { - if (unw_access_gr(&info, i, &val, &nat, 0) < 0) - return -EIO; - retval |= __put_user(val, &ppr->gr[i]); - } - - /* gr8-gr11 */ - - retval |= __copy_to_user(&ppr->gr[8], &pt->r8, sizeof(long) * 4); - - /* gr12-gr15 */ - - retval |= __copy_to_user(&ppr->gr[12], &pt->r12, sizeof(long) * 2); - retval |= __copy_to_user(&ppr->gr[14], &pt->r14, sizeof(long)); - retval |= __copy_to_user(&ppr->gr[15], &pt->r15, sizeof(long)); - - /* gr16-gr31 */ - - retval |= __copy_to_user(&ppr->gr[16], &pt->r16, sizeof(long) * 16); - - /* b0 */ - - retval |= __put_user(pt->b0, &ppr->br[0]); - - /* b1-b5 */ - - for (i = 1; i < 6; i++) { - if (unw_access_br(&info, i, &val, 0) < 0) - return -EIO; - __put_user(val, &ppr->br[i]); - } - - /* b6-b7 */ - - retval |= __put_user(pt->b6, &ppr->br[6]); - retval |= __put_user(pt->b7, &ppr->br[7]); - - /* fr2-fr5 */ - - for (i = 2; i < 6; i++) { - if (unw_get_fr(&info, i, &fpval) < 0) - return -EIO; - retval |= __copy_to_user(&ppr->fr[i], &fpval, sizeof (fpval)); - } - - /* fr6-fr11 */ - - retval |= __copy_to_user(&ppr->fr[6], &pt->f6, - sizeof(struct ia64_fpreg) * 6); - - /* fp scratch regs(12-15) */ - - retval |= __copy_to_user(&ppr->fr[12], &sw->f12, - sizeof(struct ia64_fpreg) * 4); - - /* fr16-fr31 */ - - for (i = 16; i < 32; i++) { - if (unw_get_fr(&info, i, &fpval) < 0) - return -EIO; - retval |= __copy_to_user(&ppr->fr[i], &fpval, sizeof (fpval)); - } - - /* fph */ - - ia64_flush_fph(child); - retval |= __copy_to_user(&ppr->fr[32], &child->thread.fph, - sizeof(ppr->fr[32]) * 96); - - /* preds */ - - retval |= __put_user(pt->pr, &ppr->pr); - - /* nat bits */ - - retval |= __put_user(nat_bits, &ppr->nat); - - ret = retval ? -EIO : 0; - return ret; -} -#endif /* ptrace_getregs() */ - -#if 0 -static long -ptrace_setregs (struct task_struct *child, struct pt_all_user_regs __user *ppr) -{ - unsigned long psr, rsc, ec, lc, rnat, bsp, cfm, nat_bits, val = 0; - struct unw_frame_info info; - struct switch_stack *sw; - struct ia64_fpreg fpval; - struct pt_regs *pt; - long ret, retval = 0; - int i; - - memset(&fpval, 0, sizeof(fpval)); - - if (!access_ok(VERIFY_READ, ppr, sizeof(struct pt_all_user_regs))) - return -EIO; - - pt = task_pt_regs(child); - sw = (struct switch_stack *) (child->thread.ksp + 16); - unw_init_from_blocked_task(&info, child); - if (unw_unwind_to_user(&info) < 0) { - return -EIO; - } - - if (((unsigned long) ppr & 0x7) != 0) { - dprintk("ptrace:unaligned register address %p\n", ppr); - return -EIO; - } - - /* control regs */ - - retval |= __get_user(pt->cr_iip, &ppr->cr_iip); - retval |= __get_user(psr, &ppr->cr_ipsr); - - /* app regs */ - - retval |= __get_user(pt->ar_pfs, &ppr->ar[PT_AUR_PFS]); - retval |= __get_user(rsc, &ppr->ar[PT_AUR_RSC]); - retval |= __get_user(pt->ar_bspstore, &ppr->ar[PT_AUR_BSPSTORE]); - retval |= __get_user(pt->ar_unat, &ppr->ar[PT_AUR_UNAT]); - retval |= __get_user(pt->ar_ccv, &ppr->ar[PT_AUR_CCV]); - retval |= __get_user(pt->ar_fpsr, &ppr->ar[PT_AUR_FPSR]); - - retval |= __get_user(ec, &ppr->ar[PT_AUR_EC]); - retval |= __get_user(lc, &ppr->ar[PT_AUR_LC]); - retval |= __get_user(rnat, &ppr->ar[PT_AUR_RNAT]); - retval |= __get_user(bsp, &ppr->ar[PT_AUR_BSP]); - retval |= __get_user(cfm, &ppr->cfm); - - /* gr1-gr3 */ - - retval |= __copy_from_user(&pt->r1, &ppr->gr[1], sizeof(long)); - retval |= __copy_from_user(&pt->r2, &ppr->gr[2], sizeof(long) * 2); - - /* gr4-gr7 */ - - for (i = 4; i < 8; i++) { - retval |= __get_user(val, &ppr->gr[i]); - /* NaT bit will be set via PT_NAT_BITS: */ - if (unw_set_gr(&info, i, val, 0) < 0) - return -EIO; - } - - /* gr8-gr11 */ - - retval |= __copy_from_user(&pt->r8, &ppr->gr[8], sizeof(long) * 4); - - /* gr12-gr15 */ - - retval |= __copy_from_user(&pt->r12, &ppr->gr[12], sizeof(long) * 2); - retval |= __copy_from_user(&pt->r14, &ppr->gr[14], sizeof(long)); - retval |= __copy_from_user(&pt->r15, &ppr->gr[15], sizeof(long)); - - /* gr16-gr31 */ - - retval |= __copy_from_user(&pt->r16, &ppr->gr[16], sizeof(long) * 16); - - /* b0 */ - - retval |= __get_user(pt->b0, &ppr->br[0]); - - /* b1-b5 */ - - for (i = 1; i < 6; i++) { - retval |= __get_user(val, &ppr->br[i]); - unw_set_br(&info, i, val); - } - - /* b6-b7 */ - - retval |= __get_user(pt->b6, &ppr->br[6]); - retval |= __get_user(pt->b7, &ppr->br[7]); - - /* fr2-fr5 */ - - for (i = 2; i < 6; i++) { - retval |= __copy_from_user(&fpval, &ppr->fr[i], sizeof(fpval)); - if (unw_set_fr(&info, i, fpval) < 0) - return -EIO; - } - - /* fr6-fr11 */ - - retval |= __copy_from_user(&pt->f6, &ppr->fr[6], - sizeof(ppr->fr[6]) * 6); - - /* fp scratch regs(12-15) */ - - retval |= __copy_from_user(&sw->f12, &ppr->fr[12], - sizeof(ppr->fr[12]) * 4); - - /* fr16-fr31 */ - - for (i = 16; i < 32; i++) { - retval |= __copy_from_user(&fpval, &ppr->fr[i], - sizeof(fpval)); - if (unw_set_fr(&info, i, fpval) < 0) - return -EIO; - } - - /* fph */ - - ia64_sync_fph(child); - retval |= __copy_from_user(&child->thread.fph, &ppr->fr[32], - sizeof(ppr->fr[32]) * 96); - - /* preds */ - - retval |= __get_user(pt->pr, &ppr->pr); - - /* nat bits */ - - retval |= __get_user(nat_bits, &ppr->nat); - - retval |= access_uarea(child, PT_CR_IPSR, &psr, 1); - retval |= access_uarea(child, PT_AR_RSC, &rsc, 1); - retval |= access_uarea(child, PT_AR_EC, &ec, 1); - retval |= access_uarea(child, PT_AR_LC, &lc, 1); - retval |= access_uarea(child, PT_AR_RNAT, &rnat, 1); - retval |= access_uarea(child, PT_AR_BSP, &bsp, 1); - retval |= access_uarea(child, PT_CFM, &cfm, 1); - retval |= access_uarea(child, PT_NAT_BITS, &nat_bits, 1); - - ret = retval ? -EIO : 0; - return ret; -} -#endif /* ptrace_setregs() */ - -/* - * Called by kernel/ptrace.c when detaching.. - * - * Make sure the single step bit is not set. - */ -void -ptrace_disable (struct task_struct *child) -{ - struct ia64_psr *child_psr = ia64_psr(task_pt_regs(child)); - - /* make sure the single step/taken-branch trap bits are not set: */ - child_psr->ss = 0; - child_psr->tb = 0; -} - -#if 0 /* XXX */ -asmlinkage long -sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data) -{ - struct pt_regs *pt; - unsigned long urbs_end, peek_or_poke; - struct task_struct *child; - struct switch_stack *sw; - long ret; - struct unw_frame_info info; - - lock_kernel(); - ret = -EPERM; - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); - goto out; - } - - peek_or_poke = (request == PTRACE_PEEKTEXT - || request == PTRACE_PEEKDATA - || request == PTRACE_POKETEXT - || request == PTRACE_POKEDATA); - ret = -ESRCH; - read_lock(&tasklist_lock); - { - child = find_task_by_pid(pid); - if (child) { - if (peek_or_poke) - child = find_thread_for_addr(child, addr); - get_task_struct(child); - } - } - read_unlock(&tasklist_lock); - if (!child) - goto out; - ret = -EPERM; - if (pid == 1) /* no messing around with init! */ - goto out_tsk; - - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - if (!ret) - arch_ptrace_attach(child); - goto out_tsk; - } - - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) - goto out_tsk; - - pt = task_pt_regs(child); - sw = (struct switch_stack *) (child->thread.ksp + 16); - - switch (request) { - case PTRACE_PEEKTEXT: - case PTRACE_PEEKDATA: - /* read word at location addr */ - urbs_end = ia64_get_user_rbs_end(child, pt, NULL); - ret = ia64_peek(child, sw, urbs_end, addr, &data); - if (ret == 0) { - ret = data; - /* ensure "ret" is not mistaken as an error code: */ - force_successful_syscall_return(); - } - goto out_tsk; - - case PTRACE_POKETEXT: - case PTRACE_POKEDATA: - /* write the word at location addr */ - urbs_end = ia64_get_user_rbs_end(child, pt, NULL); - ret = ia64_poke(child, sw, urbs_end, addr, data); - - /* Make sure user RBS has the latest data */ - unw_init_from_blocked_task(&info, child); - do_sync_rbs(&info, ia64_sync_user_rbs); - - goto out_tsk; - - case PTRACE_PEEKUSR: - /* read the word at addr in the USER area */ - if (access_uarea(child, addr, &data, 0) < 0) { - ret = -EIO; - goto out_tsk; - } - ret = data; - /* ensure "ret" is not mistaken as an error code */ - force_successful_syscall_return(); - goto out_tsk; - - case PTRACE_POKEUSR: - /* write the word at addr in the USER area */ - if (access_uarea(child, addr, &data, 1) < 0) { - ret = -EIO; - goto out_tsk; - } - ret = 0; - goto out_tsk; - - case PTRACE_OLD_GETSIGINFO: - /* for backwards-compatibility */ - ret = ptrace_request(child, PTRACE_GETSIGINFO, addr, data); - goto out_tsk; - - case PTRACE_OLD_SETSIGINFO: - /* for backwards-compatibility */ - ret = ptrace_request(child, PTRACE_SETSIGINFO, addr, data); - goto out_tsk; - - case PTRACE_SYSCALL: - /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: - /* restart after signal. */ - ret = -EIO; - if (!valid_signal(data)) - goto out_tsk; - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - child->exit_code = data; - - /* - * Make sure the single step/taken-branch trap bits - * are not set: - */ - ia64_psr(pt)->ss = 0; - ia64_psr(pt)->tb = 0; - - wake_up_process(child); - ret = 0; - goto out_tsk; - - case PTRACE_KILL: - /* - * 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. - */ - if (child->exit_state == EXIT_ZOMBIE) - /* already dead */ - goto out_tsk; - child->exit_code = SIGKILL; - - ptrace_disable(child); - wake_up_process(child); - ret = 0; - goto out_tsk; - - case PTRACE_SINGLESTEP: - /* let child execute for one instruction */ - case PTRACE_SINGLEBLOCK: - ret = -EIO; - if (!valid_signal(data)) - goto out_tsk; - - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - if (request == PTRACE_SINGLESTEP) { - ia64_psr(pt)->ss = 1; - } else { - ia64_psr(pt)->tb = 1; - } - child->exit_code = data; - - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; - goto out_tsk; - - case PTRACE_DETACH: - /* detach a process that was attached. */ - ret = ptrace_detach(child, data); - goto out_tsk; - - case PTRACE_GETREGS: - ret = ptrace_getregs(child, - (struct pt_all_user_regs __user *) data); - goto out_tsk; - - case PTRACE_SETREGS: - ret = ptrace_setregs(child, - (struct pt_all_user_regs __user *) data); - goto out_tsk; - - default: - ret = ptrace_request(child, request, addr, data); - goto out_tsk; - } - out_tsk: - put_task_struct(child); - out: - unlock_kernel(); - return ret; -} -#endif /* "asmlinkage" so the input arguments are preserved... */ @@ -1755,6 +850,9 @@ syscall_trace_leave (long arg0, long arg ia64_sync_krbs(); } + +#ifdef CONFIG_UTRACE + /* Utrace implementation starts here */ typedef struct utrace_get { @@ -2521,3 +1619,119 @@ const struct utrace_regset_view *utrace_ #endif return &utrace_ia64_native; } +#endif /* CONFIG_UTRACE */ + + +#ifdef CONFIG_PTRACE + +#define WORD(member, num) \ + offsetof(struct pt_all_user_regs, member), \ + offsetof(struct pt_all_user_regs, member) + num * sizeof(long) +static const struct ptrace_layout_segment pt_all_user_regs_layout[] = { + {WORD(nat, 1), 0, ELF_NAT_OFFSET}, + {WORD(cr_iip, 1), 0, ELF_CR_IIP_OFFSET}, + {WORD(cfm, 1), 0, ELF_CFM_OFFSET}, + {WORD(cr_ipsr, 1), 0, ELF_CR_IPSR_OFFSET}, + {WORD(pr, 1), 0, ELF_PR_OFFSET}, + {WORD(gr[0], 1), -1, -1}, + {WORD(gr[1], 31), 0, ELF_GR_OFFSET(1)}, + {WORD(br[0], 8), 0, ELF_BR_OFFSET(0)}, + {WORD(ar[0], 16), -1, -1}, + {WORD(ar[PT_AUR_RSC], 4), 0, ELF_AR_RSC_OFFSET}, + {WORD(ar[PT_AUR_RNAT+1], 12), -1, -1}, + {WORD(ar[PT_AUR_CCV], 1), 0, ELF_AR_CCV_OFFSET}, + {WORD(ar[PT_AUR_CCV+1], 3), -1, -1}, + {WORD(ar[PT_AUR_UNAT], 1), 0, ELF_AR_UNAT_OFFSET}, + {WORD(ar[PT_AUR_UNAT+1], 3), -1, -1}, + {WORD(ar[PT_AUR_FPSR], 1), 0, ELF_AR_FPSR_OFFSET}, + {WORD(ar[PT_AUR_FPSR+1], 23), -1, -1}, + {WORD(ar[PT_AUR_PFS], 3), 0, ELF_AR_PFS_OFFSET}, + {WORD(ar[PT_AUR_EC+1], 62), -1, -1}, + {offsetof(struct pt_all_user_regs, fr[0]), + offsetof(struct pt_all_user_regs, fr[2]), + -1, -1}, + {offsetof(struct pt_all_user_regs, fr[2]), + offsetof(struct pt_all_user_regs, fr[128]), + 1, 2 * sizeof(elf_fpreg_t)}, + {0, 0, -1, 0} +}; +#undef WORD + +#define NEXT(addr, sum) (addr + sum * sizeof(long)) +static const struct ptrace_layout_segment pt_uarea_layout[] = { + {PT_F32, PT_NAT_BITS, 1, ELF_FP_OFFSET(32)}, + {PT_NAT_BITS, NEXT(PT_NAT_BITS, 1), 0, ELF_NAT_OFFSET}, + {PT_F2, PT_F10, 1, ELF_FP_OFFSET(2)}, + {PT_F10, PT_R4, 1, ELF_FP_OFFSET(10)}, + {PT_R4, PT_B1, 0, ELF_GR_OFFSET(4)}, + {PT_B1, PT_AR_EC, 0, ELF_BR_OFFSET(1)}, + {PT_AR_EC, PT_AR_LC, 0, ELF_AR_EC_OFFSET}, + {PT_AR_LC, NEXT(PT_AR_LC, 1), 0, ELF_AR_LC_OFFSET}, + {PT_CR_IPSR, PT_CR_IIP, 0, ELF_CR_IPSR_OFFSET}, + {PT_CR_IIP, PT_AR_UNAT, 0, ELF_CR_IIP_OFFSET}, + {PT_AR_UNAT, PT_AR_PFS, 0, ELF_AR_UNAT_OFFSET}, + {PT_AR_PFS, PT_AR_RSC, 0, ELF_AR_PFS_OFFSET}, + {PT_AR_RSC, PT_AR_RNAT, 0, ELF_AR_RSC_OFFSET}, + {PT_AR_RNAT, PT_AR_BSPSTORE, 0, ELF_AR_RNAT_OFFSET}, + {PT_AR_BSPSTORE,PT_PR, 0, ELF_AR_BSPSTORE_OFFSET}, + {PT_PR, PT_B6, 0, ELF_PR_OFFSET}, + {PT_B6, PT_AR_BSP, 0, ELF_BR_OFFSET(6)}, + {PT_AR_BSP, PT_R1, 0, ELF_AR_BSP_OFFSET}, + {PT_R1, PT_R12, 0, ELF_GR_OFFSET(1)}, + {PT_R12, PT_R8, 0, ELF_GR_OFFSET(12)}, + {PT_R8, PT_R16, 0, ELF_GR_OFFSET(8)}, + {PT_R16, PT_AR_CCV, 0, ELF_GR_OFFSET(16)}, + {PT_AR_CCV, PT_AR_FPSR, 0, ELF_AR_CCV_OFFSET}, + {PT_AR_FPSR, PT_B0, 0, ELF_AR_FPSR_OFFSET}, + {PT_B0, PT_B7, 0, ELF_BR_OFFSET(0)}, + {PT_B7, PT_F6, 0, ELF_BR_OFFSET(7)}, + {PT_F6, PT_AR_CSD, 1, ELF_FP_OFFSET(6)}, + {PT_AR_CSD, NEXT(PT_AR_CSD, 2), 0, ELF_AR_CSD_OFFSET}, + {PT_DBR, NEXT(PT_DBR, 8), 2, 0}, + {PT_IBR, NEXT(PT_IBR, 8), 2, 8 * sizeof(long)}, + {0, 0, -1, 0} +}; +#undef NEXT + +int arch_ptrace(long *request, struct task_struct *child, + struct utrace_attached_engine *engine, + unsigned long addr, unsigned long data, long *val) +{ + int ret = -ENOSYS; + switch (*request) { + case PTRACE_OLD_GETSIGINFO: + *request = PTRACE_GETSIGINFO; + break; + case PTRACE_OLD_SETSIGINFO: + *request = PTRACE_SETSIGINFO; + break; + + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + ret = access_process_vm(child, addr, val, sizeof(*val), 0); + ret = ret == sizeof(*val) ? 0 : -EIO; + break; + + case PTRACE_PEEKUSR: + return ptrace_layout_access(child, engine, + utrace_native_view(current), + pt_uarea_layout, + addr, sizeof(long), + NULL, val, 0); + case PTRACE_POKEUSR: + return ptrace_pokeusr(child, engine, + pt_uarea_layout, addr, data); + + case PTRACE_GETREGS: + case PTRACE_SETREGS: + return ptrace_layout_access(child, engine, + utrace_native_view(current), + pt_all_user_regs_layout, + 0, sizeof(struct pt_all_user_regs), + (void __user *) data, NULL, + *request == PTRACE_SETREGS); + } + return ret; +} + +#endif /* CONFIG_PTRACE */