/* * Copyright (C) 2005-2012 Imagination Technologies Ltd. * * 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. */ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/errno.h> #include <linux/ptrace.h> #include <linux/user.h> #include <linux/regset.h> #include <linux/tracehook.h> #include <linux/elf.h> #include <linux/uaccess.h> #include <trace/syscall.h> #define CREATE_TRACE_POINTS #include <trace/events/syscalls.h> /* * user_regset definitions. */ int metag_gp_regs_copyout(const struct pt_regs *regs, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { const void *ptr; unsigned long data; int ret; /* D{0-1}.{0-7} */ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs->ctx.DX, 0, 4*16); if (ret) goto out; /* A{0-1}.{0-1} */ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs->ctx.AX, 4*16, 4*20); if (ret) goto out; /* A{0-1}.2 */ if (regs->ctx.SaveMask & TBICTX_XEXT_BIT) ptr = regs->ctx.Ext.Ctx.pExt; else ptr = ®s->ctx.Ext.AX2; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, ptr, 4*20, 4*22); if (ret) goto out; /* A{0-1}.3 */ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, ®s->ctx.AX3, 4*22, 4*24); if (ret) goto out; /* PC */ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, ®s->ctx.CurrPC, 4*24, 4*25); if (ret) goto out; /* TXSTATUS */ data = (unsigned long)regs->ctx.Flags; if (regs->ctx.SaveMask & TBICTX_CBUF_BIT) data |= USER_GP_REGS_STATUS_CATCH_BIT; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &data, 4*25, 4*26); if (ret) goto out; /* TXRPT, TXBPOBITS, TXMODE */ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, ®s->ctx.CurrRPT, 4*26, 4*29); if (ret) goto out; /* Padding */ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 4*29, 4*30); out: return ret; } int metag_gp_regs_copyin(struct pt_regs *regs, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { void *ptr; unsigned long data; int ret; /* D{0-1}.{0-7} */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs->ctx.DX, 0, 4*16); if (ret) goto out; /* A{0-1}.{0-1} */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs->ctx.AX, 4*16, 4*20); if (ret) goto out; /* A{0-1}.2 */ if (regs->ctx.SaveMask & TBICTX_XEXT_BIT) ptr = regs->ctx.Ext.Ctx.pExt; else ptr = ®s->ctx.Ext.AX2; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ptr, 4*20, 4*22); if (ret) goto out; /* A{0-1}.3 */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s->ctx.AX3, 4*22, 4*24); if (ret) goto out; /* PC */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s->ctx.CurrPC, 4*24, 4*25); if (ret) goto out; /* TXSTATUS */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &data, 4*25, 4*26); if (ret) goto out; regs->ctx.Flags = data & 0xffff; if (data & USER_GP_REGS_STATUS_CATCH_BIT) regs->ctx.SaveMask |= TBICTX_XCBF_BIT | TBICTX_CBUF_BIT; else regs->ctx.SaveMask &= ~TBICTX_CBUF_BIT; /* TXRPT, TXBPOBITS, TXMODE */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s->ctx.CurrRPT, 4*26, 4*29); out: return ret; } static int metag_gp_regs_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { const struct pt_regs *regs = task_pt_regs(target); return metag_gp_regs_copyout(regs, pos, count, kbuf, ubuf); } static int metag_gp_regs_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { struct pt_regs *regs = task_pt_regs(target); return metag_gp_regs_copyin(regs, pos, count, kbuf, ubuf); } int metag_cb_regs_copyout(const struct pt_regs *regs, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { int ret; /* TXCATCH{0-3} */ if (regs->ctx.SaveMask & TBICTX_XCBF_BIT) ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs->extcb0, 0, 4*4); else ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 0, 4*4); return ret; } int metag_cb_regs_copyin(struct pt_regs *regs, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { int ret; /* TXCATCH{0-3} */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs->extcb0, 0, 4*4); return ret; } static int metag_cb_regs_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { const struct pt_regs *regs = task_pt_regs(target); return metag_cb_regs_copyout(regs, pos, count, kbuf, ubuf); } static int metag_cb_regs_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { struct pt_regs *regs = task_pt_regs(target); return metag_cb_regs_copyin(regs, pos, count, kbuf, ubuf); } int metag_rp_state_copyout(const struct pt_regs *regs, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { unsigned long mask; u64 *ptr; int ret, i; /* Empty read pipeline */ if (!(regs->ctx.SaveMask & TBICTX_CBRP_BIT)) { ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 0, 4*13); goto out; } mask = (regs->ctx.CurrDIVTIME & TXDIVTIME_RPMASK_BITS) >> TXDIVTIME_RPMASK_S; /* Read pipeline entries */ ptr = (void *)®s->extcb0[1]; for (i = 0; i < 6; ++i, ++ptr) { if (mask & (1 << i)) ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, ptr, 8*i, 8*(i + 1)); else ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 8*i, 8*(i + 1)); if (ret) goto out; } /* Mask of entries */ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &mask, 4*12, 4*13); out: return ret; } int metag_rp_state_copyin(struct pt_regs *regs, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { struct user_rp_state rp; unsigned long long *ptr; int ret, i; /* Read the entire pipeline before making any changes */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &rp, 0, 4*13); if (ret) goto out; /* Write pipeline entries */ ptr = (void *)®s->extcb0[1]; for (i = 0; i < 6; ++i, ++ptr) if (rp.mask & (1 << i)) *ptr = rp.entries[i]; /* Update RPMask in TXDIVTIME */ regs->ctx.CurrDIVTIME &= ~TXDIVTIME_RPMASK_BITS; regs->ctx.CurrDIVTIME |= (rp.mask << TXDIVTIME_RPMASK_S) & TXDIVTIME_RPMASK_BITS; /* Set/clear flags to indicate catch/read pipeline state */ if (rp.mask) regs->ctx.SaveMask |= TBICTX_XCBF_BIT | TBICTX_CBRP_BIT; else regs->ctx.SaveMask &= ~TBICTX_CBRP_BIT; out: return ret; } static int metag_rp_state_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { const struct pt_regs *regs = task_pt_regs(target); return metag_rp_state_copyout(regs, pos, count, kbuf, ubuf); } static int metag_rp_state_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { struct pt_regs *regs = task_pt_regs(target); return metag_rp_state_copyin(regs, pos, count, kbuf, ubuf); } static int metag_tls_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { void __user *tls = target->thread.tls_ptr; return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); } static int metag_tls_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { int ret; void __user *tls; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); if (ret) return ret; target->thread.tls_ptr = tls; return ret; } enum metag_regset { REGSET_GENERAL, REGSET_CBUF, REGSET_READPIPE, REGSET_TLS, }; static const struct user_regset metag_regsets[] = { [REGSET_GENERAL] = { .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long long), .get = metag_gp_regs_get, .set = metag_gp_regs_set, }, [REGSET_CBUF] = { .core_note_type = NT_METAG_CBUF, .n = sizeof(struct user_cb_regs) / sizeof(long), .size = sizeof(long), .align = sizeof(long long), .get = metag_cb_regs_get, .set = metag_cb_regs_set, }, [REGSET_READPIPE] = { .core_note_type = NT_METAG_RPIPE, .n = sizeof(struct user_rp_state) / sizeof(long), .size = sizeof(long), .align = sizeof(long long), .get = metag_rp_state_get, .set = metag_rp_state_set, }, [REGSET_TLS] = { .core_note_type = NT_METAG_TLS, .n = 1, .size = sizeof(void *), .align = sizeof(void *), .get = metag_tls_get, .set = metag_tls_set, }, }; static const struct user_regset_view user_metag_view = { .name = "metag", .e_machine = EM_METAG, .regsets = metag_regsets, .n = ARRAY_SIZE(metag_regsets) }; const struct user_regset_view *task_user_regset_view(struct task_struct *task) { return &user_metag_view; } /* * Called by kernel/ptrace.c when detaching.. * * Make sure single step bits etc are not set. */ void ptrace_disable(struct task_struct *child) { /* nothing to do.. */ } long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { int ret; switch (request) { default: ret = ptrace_request(child, request, addr, data); break; } return ret; } int syscall_trace_enter(struct pt_regs *regs) { int ret = 0; if (test_thread_flag(TIF_SYSCALL_TRACE)) ret = tracehook_report_syscall_entry(regs); if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->ctx.DX[0].U1); return ret ? -1 : regs->ctx.DX[0].U1; } void syscall_trace_leave(struct pt_regs *regs) { if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs->ctx.DX[0].U1); if (test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall_exit(regs, 0); }